2017Summmer_上海金马五校 F题,G题,I题,K题

以下题目均自己搜

F题  A序列

一开始真的没懂题目什么意思,还以为是要连续的子串,结果发现时序列,简直智障,知道题意之后,好久没搞LIS,有点忘了,复习一波以后,直接双向LIS,处理处两个数组L和R,然后对整个数组扫一遍对于每一个下标取m=min(L[i],R[i]);用ans取2*m-1中的最大值。LIS用nlogn的算法实现,二分用的是lower_bound(),直接看代码。

//Author: xiaowuga
#include <bits/stdc++.h>
#define maxx INT_MAX
#define minn INT_MIN
#define inf 0x3f3f3f3f
const long long N=500003;
using namespace std;
typedef long long LL;
int dp[N];
int L[N],R[N];
int main() {
    ios::sync_with_stdio(false);cin.tie(0);
    int n;
    int a[N];
    while(cin>>n){
        for(int i=0;i<n;i++){
            cin>>a[i];
        }
        memset(dp,inf,sizeof(dp));
        for(int i=0;i<n;i++){
            int pos=lower_bound(dp,dp+n,a[i])-dp;
            dp[pos]=a[i];
            L[i]=pos+1;
        }

        memset(dp,inf,sizeof(dp));
        for(int i=n-1;i>=0;i--){
            int pos=lower_bound(dp,dp+n,a[i])-dp;
            dp[pos]=a[i];
            R[i]=pos+1;
        }
         for(int i=0;i<n;i++) cout<<L[i]<<" "; cout<<endl;
          for(int i=0;i<n;i++) cout<<R[i]<<" "; cout<<endl;
        int ans=minn;
        int tmp;
        for(int i=0;i<n;i++){
            int tmp=min(L[i],R[i]);
            ans=max(tmp*2-1,ans);
        }
        cout<<ans<<endl;
    }
    return 0;
}

G题 战斗

暴力阶乘题,因为n<10,所以暴力枚举全排列所有的出战顺序,然后模拟和电脑去打就好了,复杂度n*n!。不要真的老实的的每一次攻击的去模拟,万一两个两个怪兽都是1000的血1的攻击力,一次枚举,十个怪兽就是10000的计算计算量,而你的枚举量可能高达10!三百多万啊,绝逼超时GG,所以直接除找到每组队长的怪兽需要各自攻击对方几次才会死,然后取最小值,将hp减去攻击次数*攻击力,得到怪兽战后的状态,对于hp<=0的我们就换人。还有不要确切的计算具体要攻击多少次,因为有的时候比如你是9的hp,5的五的攻击力,9/5=1,实际上要攻击两次才会死攻击次数是hp/at+1。然后如果双方的hp变成10,10/5=2,刚好死亡攻击次数恰好是hp/at,这个时候如果用%取判断了话,是会超时的,因为%与运算是比较慢的。所以我们用一个while循环,模拟最后一次攻击,将会比算出这个具体的攻击次数速度要快。这就是我为什么一开始超时的原因,还有一个优化就是要枚举的时候都要复制一遍双方的怪兽信息,因为只有当前的怪兽有用,所以我们用一个值来存就好了,如果你用结构体,每次就有一个攻击实际上不用复制但是复制了,实际上浪费了时间,面对10!的枚举量,也会慢很多,然后就是边打边复制,这样有的时候电脑其实一只怪兽就团灭你了,但是你却多复制了其他怪兽同样浪费时间。

最后还有一个逆天优化:就是我们每次记住上一次枚举出场的顺序,如果电脑两只怪兽就把你团灭了,那么如果你下一次枚举前两只怪兽出场顺序没有发生改变,那么电脑还是可以用两只怪兽把你团灭,无论你后面的怪兽如何出出站,压倒性的实力。这样的枚举是无用的,如果对他的战斗进行模拟,又会有大量的复制会是无用功,而且对于字典序全排列,大多数的枚举的前几位都和上一次枚举基本是一样的。这就给我们启发,使我们可以快速的跳过一下无用的枚举,将n*n!的复杂度,优化成n!,加快了近十倍的速度。大概只需要100ms就可以ac,当然后还有2^n复杂度的做法,那个就更快了,需要用到状态压缩dp,本人目前不会。

//Author: xiaowuga
#include <bits/stdc++.h>
#define maxx INT_MAX
#define minn INT_MIN
#define inf 0x3f3f3f3f
const long long N=11;
using namespace std;
typedef long long L;
struct M{
    int at,hp,pos;
    bool operator <(const M &m) const{
        return at<m.at;
    }
}me[N],co[N];
M cm[N],mm[N];
int main() {
    int T,n;
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        for(int i=0;i<n;i++) scanf("%d%d",&co[i].hp,&co[i].at);
        for(int i=0;i<n;i++) {scanf("%d%d",&me[i].hp,&me[i].at);}
        int flag=0,flag2=0;
        int c1=0,c2=0,m;
        sort(me,me+n);
        do{
            if(flag2){
                if(c1==0) break;
                else{
                    int ifsame=1;
                    for(int i=0;i<=c1;i++){
                        if(me[i].at!=mm[i].at) {ifsame=0;break;}
                    }
                    if(ifsame) continue;
                 }
            }
            flag2=1;
            c1=0;c2=0;
            cm[0]=co[0]; mm[0]=me[0];
            while(c1<n&&c2<n){
                m=min(cm[c1].hp/mm[c2].at,mm[c2].hp/cm[c1].at);
                cm[c1].hp-=m*mm[c2].at;
                mm[c2].hp-=m*cm[c1].at;
                while(cm[c1].hp>0&&mm[c2].hp>0){
                    cm[c1].hp-=mm[c2].at;
                    mm[c2].hp-=cm[c1].at;
                }
                if(cm[c1].hp<=0) {c1++;cm[c1]=co[c1];}
                if(mm[c2].hp<=0) {c2++;mm[c2]=me[c2];}
            }
            if(c1==n&&c2<n) {flag=1;break;}
        }while(next_permutation(me,me+n));
        if(flag) printf("YES\n");
        else printf("NO\n");
    }
    return 0;
}

I题   丢史蒂芬妮

博弈xjb搜题,博弈论知识不好,别人告诉我怎么搜的。xjb搜了一波,就过了,不是特别理解。直接代码吧!

//Author: xiaowuga
#include<cstdio>
#define maxx INT_MAX
#define minn INT_MIN
#define inf 0x3f3f3f3f
const long long N=505;
using namespace std;
typedef long long L;
int vis1[N]={0};
int num=0;
int primnum_list[N];
void make_primnum(){
    for(int i=2;i<N/2;i++)
        for(int j=2*i;j<N;j+=i){
            vis1[j]=1;
        }
    for(int i=2;i<N;i++){
        if(vis1[i]==0) primnum_list[num++]=i;
    }
}
int mat[N][N]={0},vis[N][N]={0};
int dfs(int x,int y){
    if(vis[x][y]) return mat[x][y];
    vis[x][y]=1;
    for(int i=0;i<num;i++){
        int t=primnum_list[i];
        if(x-t>0) mat[x][y] |=!(dfs(x-t,y));
        if(y>t>0) mat[x][y] |=!(dfs(x,y-t));
        if(x-t>0&&y-t>0) mat[x][y] |=!(dfs(x-t,y-t));
    }
    return mat[x][y];
}
int main() {
    make_primnum();
    for(int i=1;i<=500;i++)
       for(int j=1;j<=500;j++){
            mat[i][j]=dfs(i,j);
       }
    int T;
    scanf("%d",&T);
   while(T--){
        int n,m;
        scanf("%d%d",&n,&m);
        if(mat[n][m]) printf("Sora\n");
        else printf("Shiro\n");
   }
    return 0;
}

K题   购买装备

第一眼尼玛不是一个背包dp吗?还是0-1背包,结果native了,10000的物品,一亿的预算,背包的复杂度更本跑不出来,所以我们采用贪心的思路。

我们首先要把问题分开去思考,这样有助于我们思考,而不会因为问题整体比较比较复杂,从而在思考上停滞不前,应该要把问题什么事主要问题什么是次要问题,题目中次要问题,实在主要问题的限制之下,也就是在购买尽量多的物品的情况下,物品中属性最小的值尽量大。首先是购买尽量多的物品,由于每个物品只能买一次,所以买便宜的物品,剩下的钱更多,也就可以买更多的物品,所以贪心对价格排序得到最大可以购买的数量m,但是这不一定是最小属性最大的。所以我们接着看后半部分的问题,问题就变成了在n个物品里面选m个其中属性最小的的物品的值要尽量大,标准的最小值最大化的套路。对属性排序,二分枚举属性,在大于等于当前枚举的最小属性中,对价格排序取 前m个,判断是否小于等于预算,这个是学长的做法,我也想过。但是每次复制一遍再排序,所以觉得没可行,所以没敢试。谁知学长用了特殊姿势。用了nth_element()这个库函数,使用m次,直接排序复杂度nlogn,减去复制的过程防止复杂度上升到n^2logn,从而使复杂度变为nlognlogn,这个复杂度可以满足题目的数据量。但是我想到是另一个种方法,前面和学长一样得到m的最大购买数量,然后对属性排序(从大到小),取前m个元素判断是否满足预算(枚举下标为m-1的元素为最小属性),不满足则枚举下标为m的元素为最小属性,从前m+1个元素里取m个最便宜的看一下,是否满足预算,不满足再次往后枚举,直到满足要求,我也需要复制一遍再排序,然而我想到用优先队列优化,建立一个大小为m的优先队列,计算一个堆的价格总和,然后每次枚举我们把堆顶元素pop,将枚举的元素插入,计算总价格的改变值,判断是否满足要求,直到满足要求位置。由于删除和插入的操作复杂度都是logn,所以总体的复杂度是nlogn,以下是我的代码

//Author: xiaowuga
#include <bits/stdc++.h>
#define maxx INT_MAX
#define minn INT_MIN
#define inf 0x3f3f3f3f
const long long N= 1e5+10;
using namespace std;
typedef long long L;
priority_queue<long long, vector<long long>,less<long long> >q;
struct equip{
    long long a,b;
}oj[N];
bool cmp1(equip x,equip y){
    return x.b<y.b;
}
bool cmp2(equip x,equip y){
    return x.a>y.a;
}
int main() {
    long long T,n,m;
    scanf("%lld",&T);
    while(T--){
        scanf("%lld%lld",&n,&m);
        for(int i=0;i<n;i++) scanf("%lld%lld",&oj[i].a,&oj[i].b);
        sort(oj,oj+n,cmp1);
        long long sum=0,k=0;
        for(int i=0;i<n;i++){
            if(sum+oj[i].b<=m){sum+=oj[i].b;k++;}
            else break;
        }
        sort(oj,oj+n,cmp2);
        sum=0;
        while(!q.empty()) q.pop();
        for(int i=0;i<k;i++){
            q.push(oj[i].b);
            sum+=oj[i].b;
        }
        if(sum<=m) {printf("%lld %lld\n",k,oj[k-1].a);continue;}
        for(int i=k;i<n;i++){
            sum=sum-q.top()+oj[i].b;
            if(sum<=m){ printf("%lld %lld\n",k,oj[i].a); break; }
            q.pop();q.push(oj[i].b);
        }
    }
    return 0;
}

继续补题,这个随笔将会在最近持续更新,尽量把金马五校的题补完吧!!!

时间: 2024-10-14 11:21:45

2017Summmer_上海金马五校 F题,G题,I题,K题的相关文章

2017年上海金马五校程序设计竞赛

A STEED 这个字符串可以任意变换位子 找到第n个 深搜 遍历所有可能字符串 然后放到set维护  第n个就可以了 #include<stdio.h> #include<algorithm> #include<string.h> #include<string> #include<stdlib.h> #include<set> #include<iterator> #include<iostream> us

2018上海高校金马五校赛训练日志

solve  5(A  E  F  I  L) rank  77 水题总体没有很卡,但提升的题都没有思路,实力差距还是有的. 个人感觉出了的题都是铜牌及以下的难度. A Wasserstein Distance <qj> 思路: 已知两堆的值,我求出它们的差,如果是正数,说明原始堆要挪走一部分:否则,则要从别的堆移一部分过来. 不难证明,为了使花费最小,直接从左到右扫一遍便可,这样一定最优. 两个优先队列,存下标和差值.扫一遍就过了. B 合约数 C 序列变换 D  数字游戏 E  小Y吃苹果

埃森哲杯第十六届上海大学程序设计联赛春季赛暨上海高校金马五校赛 B合约数

链接:https://www.nowcoder.com/acm/contest/91/B来源:牛客网牛客网没有账号的同学,请用这个注册,支持楼主,谢谢. 题目描述 给定一棵n个节点的树,并且根节点的编号为p,第i个节点有属性值vali, 定义F(i): 在以i为根的子树中,属性值是vali的合约数的节点个数.y 是 x 的合约数是指 y 是合数且 y 是 x 的约数.小埃想知道对1000000007取模后的结果. 输入描述: 输入测试组数T,每组数据,输入n+1行整数,第一行为n和p,1<=n<

“盛大游戏杯”第15届上海大学程序设计联赛夏季赛暨上海高校金马五校赛 B

<神无月>作为盛大游戏2017年的全新原创大作,其开发团队在自研实力强大的传世工作室基础之上,还有美树本晴彦等日本一线知名画师及日本游戏音乐大师崎元仁加盟参与制作.目前正在不限号内测中,有很多玩家进入到神无月的世界中. 在神无月中,有着玩家之间切磋的排位赛,其段位主要分为五大段位,从低到高依次为:新兵.菁英.战将.统帅.王者.每个玩家只有从新兵段位慢慢努力,一点点晋级才能到达王者段位.成为一个王者是每一个玩家的追求和心愿. 假设神无月的段位系统如下: 从低到高的段位依次简记为:D.C.B.A.

我的第一场比赛——金马五校赛

这场比赛在东华大学举办,基于我上次普及组初赛没有过(本人过于蒟蒻),这算是我的第一场正式线下比赛. 我们学校信息组总共有九个人报名参加,我是其中第八名,但是在整场比赛中大约五五开. 以下是我的比赛心得: 这次比赛是在是发挥失常了.热身赛的时候排名还不错,三十几名,但是到了正式比赛就不行了.这场考试共十二道题目只做出2道. 第一题应该是很水的,但是我少考虑了一个条件,查了将近两个小时后才发现错误. 第二道和最后一道题目都可以用暴力做,但是也不知道为什么就是不让我过. 第六道题目是做的最快的题目,用

【记录】写题一时爽,一直写题一直爽

//小声嘀咕 这是xk姐姐那里偷来的补题模板 感觉很棒 先借鉴用一下   初始时间 A B C D E F G H I J K L M     过题数/总题数 2019ZSTU四月月赛 04/21 L                    / / /  1/10                                                                                                                      

hdu5080:几何+polya计数(鞍山区域赛K题)

/* 鞍山区域赛的K题..当时比赛都没来得及看(反正看了也不会) 学了polya定理之后就赶紧跑来补这个题.. 由于几何比较烂写了又丑又长的代码,还debug了很久.. 比较感动的是竟然1Y了.. */ 题目大意: 给定一些点,某些点上有边,问用k种颜色染色的等价类有多少种 思路: 由于坐标是整数..只有可能旋转90,180,270才能得到置换 且图形必须为中心对称图形 先用几何方法找出对称中心 然后旋转,找是否出现置换... 由于点数只有50,几何预处理这一部分可以很暴力无脑的写..各种判断相

经典算法题每日演练——第十五题 并查集

原文:经典算法题每日演练--第十五题 并查集 这一篇我们看看经典又神奇的并查集,顾名思义就是并起来查,可用于处理一些不相交集合的秒杀. 一:场景 有时候我们会遇到这样的场景,比如:M={1,4,6,8},N={2,4,5,7},我的需求就是判断{1,2}是否属于同一个集合,当然实现方法 有很多,一般情况下,普通青年会做出O(MN)的复杂度,那么有没有更轻量级的复杂度呢?嘿嘿,并查集就是用来解决这个问题的. 二:操作 从名字可以出来,并查集其实只有两种操作,并(Union)和查(Find),并查集

[经典算法题]寻找数组中第K大的数的方法总结

[经典算法题]寻找数组中第K大的数的方法总结 责任编辑:admin 日期:2012-11-26 字体:[大 中 小] 打印复制链接我要评论 今天看算法分析是,看到一个这样的问题,就是在一堆数据中查找到第k个大的值. 名称是:设计一组N个数,确定其中第k个最大值,这是一个选择问题,当然,解决这个问题的方法很多,本人在网上搜索了一番,查找到以下的方式,决定很好,推荐给大家. 所谓"第(前)k大数问题"指的是在长度为n(n>=k)的乱序数组中S找出从大到小顺序的第(前)k个数的问题.