上海大学金马5校比赛部分题解

来源:互联网 发布:铁通和广电网络哪个好 编辑:程序博客网 时间:2024/06/08 07:45

题目链接
G
战斗

发布时间: 2017年7月8日 21:22 最后更新: 2017年7月10日 00:24 时间限制: 2000ms 内存限制: 128M

描述

最近,盛大计划开发一款手游,以下是简化版。系统和我方各有n 头怪兽,每一头怪兽都有生命值和攻击力,并且当怪兽A攻击怪兽B,如果怪兽B的生命值高于怪兽A的攻击力,则怪兽B的生命力减少A的攻击力的数值,否则怪兽B将死亡。我方已经通过一些手段得知了系统怪兽的出战序列,我方想要知道,我方是否可以合理安排怪兽的出战序列,保证当系统的n 头怪兽全部死亡时,而我方至少还存在一头怪兽。

所有怪兽是每秒攻击一次,即如果A和B战斗,A受到B的伤害的同时,B也受到A的伤害,直到一方死亡,换序列中的下一个怪兽,继续战斗。

输入
第一行一个整数T ,表示测试组数。
对于每组数据,第一行输入一个整数n ,1<=n<=10 , 表示怪兽的数目。
接下来n 行,表示系统n 头怪兽的出战序列,每一行两个整数v ,a , 1<=v<=1000 , 1<=a<=100 . 其中v 表示生命值,a 表示攻击力。
接下来n 行,表示我方n 头怪兽,但是出战序列可以由我方自己安排。每行两个整数,含义类似。

输出
每组数据输出一行。如果我方可以通过合理安排怪兽的出战序列,保证当系统的n 头怪兽全部死亡,而我方至少还存在一头怪兽,那么输出YES;否则输出NO

题解思路:因为我们要用最少的血量大最多的攻击力所以将我方怪兽按攻击力从大到小排序然后生命力按从小到大排序然后跟系统怪兽对打判断是否能赢即可

#include<iostream>#include<cstring>#include<ctime>#include<cstdlib>#include<cstdio>#include<vector>#include<algorithm>using namespace std;struct node{    int x,y;    bool operator<(node a)const{        if(y != a.y)    return y > a.y;        return x < a.x;    }}a[15],b[15];int n;int flag;bool check(){    node b1[15],b2[15];    for(int i = 0; i < n; i++){        b1[i] = a[i];        b2[i] = b[i];    }    int k = 0,j = 0;    for(;k<n&&j<n;){        int s1 = (b1[j].x-1)/b2[k].y+1;        int s2 = (b2[k].x-1)/b1[j].y+1;        if(s1>s2)            b1[j].x-=s2*b2[k].y,k++;         else if(s2>s1)            b2[k].x-=s1*b1[j].y,j++;        else    k++,j++;    }    return k < n;}int main(){    int t;    scanf("%d",&t);    while(t--){        scanf("%d",&n);        for(int i = 0; i < n; i++)            scanf("%d%d",&a[i].x,&a[i].y);        for(int i = 0; i < n; i++)            scanf("%d%d",&b[i].x,&b[i].y);        sort(b,b+n);        check()?puts("YES"):puts("NO");    }    return 0;}

H
调和序列

发布时间: 2017年7月8日 21:27 最后更新: 2017年7月8日 23:22 时间限制: 1000ms 内存限制: 128M

描述

给定一个长度为n 的非负整数序列,下标为0 ,1 ,…,n−1 .

定义:sequence(K) : 由下标为K 的倍数组成的子序列,即下标为0 ,K ,2K ,…,[n−1/k]∗k

query(K,S) : 询问sequence(K) 中的第S 大的数字

输入
第一行一个整数T ,表示测试组数。
对于每组数据,第一行输入两个整数n ,m ,1<=n<=20000 , 1<=m<=100000 ,n 表示序列的长度,m 表示询问个数。
接下来一行是n 个整数a 0 ,a 1 ,..,a n−1 ,0<=a i <2 31 , i=0,1,…,n−1 ,表示序列。
接下来m 行,每行两个整数K,S
0 < K <= 10 9 , 1<=S<=n

输出
每组数据对于每个询问输出一行,若sequence(K) 的元素个数小于S ,输出−1 ;否则输出query(K,S)

题解思路:用vector容器存k>=1&&k

#include<iostream>#include<cstring>#include<algorithm>#include<cstdio>#include<set>#include<vector>using namespace std;const int mx = 20005;int n,m;int a[mx];vector<int>v[mx];bool cmp(int a,int b){    return a>b;}int main(){    int t;    scanf("%d",&t);    while(t--){        scanf("%d%d",&n,&m);        for(int i = 0; i < n; i++){            scanf("%d",&a[i]);            v[i].clear();        }        for(int i = 1; i < n; i++){            for(int j = 0; j < n; j+=i)                v[i].push_back(a[j]);            sort(v[i].begin(),v[i].end(),cmp);        }        while(m--){            int k,s;            scanf("%d%d",&k,&s);            if(k>=n){                if(s>1)    puts("-1");                else printf("%d\n",a[0]);            }            else{                if(s>v[k].size())    puts("-1");                else printf("%d\n",v[k][s-1]);            }        }    }    return 0;}

I
丢史蒂芬妮

发布时间: 2017年7月8日 21:37 最后更新: 2017年7月8日 23:24 时间限制: 1000ms 内存限制: 128M

描述

有一天,空和白很无聊,决定玩盛大游戏,考虑到两个人玩,他们随便掏了一个游戏出来:在一个n∗m 的棋盘上,首先把史蒂芬妮·多拉放在左上角(1,1) 的位置。每次一个人可以将她往下,往右,往右下丢一格。当前回合,谁不能丢史蒂芬妮,谁就输了。(注意,不可以把活人丢出棋盘啦!)游戏总是空先手。

白说,这是一个垃圾游戏!我们每次把史蒂芬妮丢素数个位置吧!(换句话说,每次丢2 或3 或5 或7 或…格)空答应了。

我们都知道,空和白都很聪明,不管哪方存在一个可以必胜的最优策略,都会按照最优策略保证胜利。

玩了一局,空已经知道了这个游戏的套路,现在他决定考考你,对于给定的n 和m ,空是赢是输?如果空必胜,输出“Sora”(无引号);反之,输出“Shiro”(无引号)。

输入
第一行有一个T表示数组组数,1<=T<100000
从第二行开始,每行为棋盘大小,n 、m 分别表示行列。
1=

#include<iostream>#include<algorithm>#include<cstring>#include<cstdio>using namespace std;int p[505];int vis[1005][1005];void init(){    memset(vis,-1,sizeof(vis));    for(int i = 2; i < 505; i++){        if(!p[i])            p[++p[0]] = i;            for(int j = 2*i; j < 505; j+=i)                p[j] = 1;         }//    for(int i = 1; i <= p[0]; i++)    //    printf("%d\n",p[i]);    vis[1][1] = vis[1][2] = vis[2][1] = vis[2][2] = 1;    for(int i = 1; i <= 500; i++)        for(int j = 1; j <= 500; j++){            if(vis[i][j] == -1)                vis[i][j] = 1;            if(vis[i][j] == 1)            for(int k = 1; k <= p[0]; k++){                    vis[i+p[k]][j] = !vis[i][j];                    vis[i][j+p[k]] = !vis[i][j];                    vis[i+p[k]][j+p[k]] = !vis[i][j];            }        }}int main(){    init();    int t;    scanf("%d",&t);    while(t--){        int n,m;//        for(int i = 1; i <= 500; i++)//            for(int j = 1; j <= 500; j++)//            if(vis[i][j] == -1)            //        printf("%d %d\n",i,j);        scanf("%d%d",&n,&m);        if(vis[n][m])    puts("Shiro");        else puts("Sora");    }    return 0;}

K

购买装备

发布时间: 2017年7月8日 21:50 最后更新: 2017年7月8日 23:26 时间限制: 1000ms 内存限制: 128M

描述

最近盛大的一款游戏传奇世界极其火爆。游戏玩家John,想购买游戏中的装备。已知游戏的商店里有n 件装备,第i 件装备具有属性值a i ,购买需要花费b i 个金币。John想去购买这些装备,但是账号中只有m 个金币,John是个很贪婪的家伙,他想购买尽可能多的装备。并且在保证购买到最多件装备的情况下,他还想让他所购买的装备当中拥有最小属性值的装备的属性值尽可能大。

输入
输入测试组数T ,每组数据第一行输入整数n (1<=n<=100000 )和m (1<=m<=10 9 ), 接下来有n 行,第i 行有两个数a i , b i (1<=a i ,b i <=10000 ).

输出
对于每组数据,输出两个数字,第一个数字代表John最多可以购买的装备数,第二个数代表在John购买最多件装备的前提下,所购买的装备当中拥有最小属性值的装备的最大属性值(输入数据保证至少可以购买一件装备)

题解思路:先把装备按金钱从小到大排序然后求最多可以买多少个装备然后二分进行查找看最大能买最小的是多少即可

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>using namespace std;const int mx = 1e5+5;struct node{    int x,y;    bool operator<(node a)const{        if(y != a.y)    return y<a.y;        return a.x > x;    }}a[mx];int n,m,num;bool check(int x){    int ans = m;    int k = 0;    for(int i = 0; i < n; i++)        if(a[i].x >= x && a[i].y <= ans)            k++,ans-=a[i].y;    if(k >= num)        return true;    return false;}int main(){    int t;    scanf("%d",&t);    while(t--){        scanf("%d%d",&n,&m);        for(int i = 0; i < n; i++)            scanf("%d%d",&a[i].x,&a[i].y);        sort(a,a+n);        int ans = m;        num = 0;        for(int i = 0; i < n; i++)            if(ans >= a[i].y)                num++,ans-=a[i].y;            else                break;        int l = 1,r = 10001;        while(l < r){            int mid = (l+r+1)/2;            if(check(mid))                l = mid;            else                r = mid-1;        }        printf("%d %d\n",num,l);    }    return 0;}

M
风力观测

发布时间: 2017年7月8日 22:05 最后更新: 2017年7月8日 23:29 时间限制: 1000ms 内存限制: 128M

描述

小Y正在观测y地区的风力情况,他在一条直线上依此设定了n 个观测点,并观测与直线垂直方向的风力值,风力有时是正向的也有时是反向的,规定正向时的风力值为正数,他发现每次风力值的变化都可以表示为观测点上一条线段[L,R] 上的同时增强或者减弱。小Y希望能够实时统计这些观测点的数据,并且实时分析这些观测点在历史中到达的风力最大绝对值,但是他无法同时对大量的观测点进行分析, 更重要的是他记不住这些观测点过去的风力大小,于是他希望你来用计算机帮助他完成这个任务。

你简化了这个问题,将问题分为两种查询:

1.对观测点[L,R] 上的风力正向增强X 。(X 为负数表示正向减弱,即反向加强)

2.查询观测点A 上的历史风力最大绝对值。

输入
第一行有一个整数T 表示数据组数。(T<=10 )
接着有T 组数据,每组数据第一行是整数n 和q ,表示观测点个数和查询次数。
第二行有n 个数a 1 ,…,a n ,表示每个观测点的风力初始值。
接着有q 行,表示q 次操作,格式为:
1 L R X:表示对[L,R] 线段上的正向风力同时增强x 。
2 A:表示查询A 点的历史风力最大绝对值。
1<=n,q<=100000 。
1<=L,R,A<=n
−10000<=a i , X<=10000

输出
对每次询问2,输出一个数字表示风力值并换行。

题解思路:用线段树更新然后这个线段的历史最小历史最大值,如果用lazy思想更新是个难点

一开始把全部数值清0,然后用懒人思路如果该线段被更新那就求历史最低值和历史最高值也就是MIN和MAX,然后如何用lazy思想更新呢就下一段线段的历史最高等于上一段的历史最高加上下面一段还没有更新的lazy取两者最大,min的一样

#include<iostream>#include<cstring>#include<cstdio>#include<map>#include<algorithm>using namespace std;#define mid (L+R)/2#define ls 2*rt#define rs 2*rt|1#define lson ls,L,mid#define rson rs,mid+1,Rconst int mx = 1e5+5;typedef pair<int,int> P;int MAX[mx<<3],MIN[mx<<3],lazy[mx<<3];int a[mx];int n,m;void push_down(int rt){    MAX[ls] = max(MAX[ls],MAX[rt]+lazy[ls]);    MIN[ls] = min(MIN[ls],MIN[rt]+lazy[ls]);    MIN[rs] = min(MIN[rs],MIN[rt]+lazy[rs]);    MAX[rs] = max(MAX[rs],MAX[rt]+lazy[rs]); //因为上面的lazy已经更新过了所以再给下面用一遍会重复所以不只要增加还未更新的即可    lazy[rs] += lazy[rt];    lazy[ls] += lazy[rt];    lazy[rt] = MAX[rt] = MIN[rt] = 0;}void update(int rt,int L,int R,int l,int r,int v){    if(l<=L && R <= r){        lazy[rt]+=v;        MAX[rt] = max(MAX[rt],lazy[rt]);  //这一段如果没有被更新过就是直接这样        MIN[rt] = min(MIN[rt],lazy[rt]); //如果这一段被更新过的话再更新的时候只要取历史的最高还是一样的结果        return;    }    if(MAX[rt]||MIN[rt]||lazy[rt]) //如果其中有一个不为0那就更新        push_down(rt);    if(l>mid)   update(rson,l,r,v);    else if(r<=mid) update(lson,l,r,v);    else{        update(rson,mid+1,r,v);        update(lson,l,mid,v);    }}P query(int rt,int L,int R,int x){    if(L == R && L == x)        return {MAX[rt],MIN[rt]};    if(MAX[rt]||MIN[rt]||lazy[rt])        push_down(rt);    if(x > mid) return query(rson,x);    else return query(lson,x);}int main(){    int t;    scanf("%d",&t);    while(t--){        memset(MAX,0,sizeof(MAX));        memset(MIN,0,sizeof(MIN));        memset(lazy,0,sizeof(lazy));        scanf("%d%d",&n,&m);        for(int i = 1; i <= n; i++)            scanf("%d",&a[i]);        while(m--){            int casei;            scanf("%d",&casei);            if(casei == 1){                int l,r,v;                scanf("%d%d%d",&l,&r,&v);                update(1,1,n,l,r,v);            }            else{                int x;                scanf("%d",&x);                P ans = query(1,1,n,x);                    printf("%d\n",max(abs(a[x]+ans.first),abs(a[x]+ans.second)));            }        }    }    return 0;}

O
随机传送迷宫

发布时间: 2017年7月8日 22:13 最后更新: 2017年7月8日 22:15 时间限制: 1000ms 内存限制: 128M

描述

小Y做了一个随机迷宫生成器,生成的迷宫可以用n∗m 的矩阵来表示,人物可以从迷宫的起点开始,每个单位时间选择往上下左右走一步,但不能走到不能通行的格子或者走出矩阵外。在迷宫中会有不定数量的传送门,其中传送门入口与传送门出口一一对应,如果人物处在传送门入口处,可以用一个单位的时间使用这个传送门入口到达一个传送门出口处,但是在尝试之前你并不知道哪一个传送门入口对应哪一个出口,因此小Y想知道用什么样的策略走才能在最坏情况下花费最少时间到达迷宫的出口。传送门只能单向通行,但是可以重复使用,由于传送门入口出口一一对应,因此重复使用传送门会到达同一个传送门出口。

输入
第一行有一个整数T 表示数据组数。(T<=50 )
接着有T 组数据,每组数据第一行有两个整数n 、m 。(2<=n ,m<=100 )
接着有n 行,m 列的迷宫矩阵。
其中字符’S’,’T’分别表示唯一的迷宫起点和终点,
字符’#’表示无法通行的障碍,
字符’.’表示空地,
字符’i’,’o’分别表示传送门入口和出口。(传送门入口与出口数量相同,并且数量都不超过5)

输出
对于每组数据输出一行,表示花费的最少单位时间,如果不存在一定能走到出口的方案,则输出-1。

题解思路:在队友的指导下碰到第一个传送点传送后肯定最优因为你传送完第一个第二个就不用传了递归下去还是跟之前一样的情况然后再未知的情况下有传送点那么就是传送点中最恶劣情况下的最少走的步数然后如果不用传送阵能走到的话就是min(不用,用)

然后就是BFS注意因为可能重复走来走去所以要设最大步数是1e6

#include<iostream>#include<cstring>#include<cstdio>#include<queue>using namespace std;int mark[205];char map[205][205];int num[205][205];struct pos{    int x,y,t;}a[205];int x_move[4] = {0,0,1,-1};int y_move[4] = {-1,1,0,0};int len1,len,n,m;int sx,sy,ex,ey,ans;bool check(int x,int y,bool vis[][205]){    return x >= 0 && y >= 0 && x < n && y < m && !vis[x][y] && map[x][y] !='#';}int bfs(int sx,int sy){    pos ft,pt;    ft.x = sx;    ft.y = sy;    ft.t = 0;    bool vis[205][205];    memset(vis,0,sizeof(vis));    vis[sx][sy] = 1;    queue<pos>q;    q.push(ft);    int flag = 0;    int ans = 1000005;    while(!q.empty()){        ft = q.front();        if(ft.x == ex && ft.y == ey)            return min(ans,ft.t);        q.pop();        for(int i = 0;i < 4; i++){            pt.x = ft.x+x_move[i];            pt.y = ft.y+y_move[i];            if(check(pt.x,pt.y,vis)){                pt.t = ft.t+1;                if(map[pt.x][pt.y] == 'i'&&!flag)                    for(int j = 0; j < len; j++)                        if(!mark[j]){                            mark[j] = 1;                            map[pt.x][pt.y] = '.';    //                        num[pt.x][pt.y] = 1;                            if(!flag)    ans = pt.t+bfs(a[j].x,a[j].y)+1,flag = 1;                            else ans = max(ans,pt.t+bfs(a[j].x,a[j].y)+1);                            //cout<<ans<<endl;                            mark[j] = 0;                            map[pt.x][pt.y] = 'i';                        }                vis[pt.x][pt.y] = 1;                q.push(pt);            }        }    }    return ans;}int main(){    int t;    scanf("%d",&t);    while(t--){        ans = len = 0;        memset(mark,0,sizeof(mark));        memset(num,0,sizeof(num));        scanf("%d%d",&n,&m);        for(int i = 0; i < n; i++){            scanf("%s",map[i]);            for(int j = 0; j < m; j++)                if(map[i][j] == 'o'){                    a[len].x = i;                    a[len].y = j;                    len++;                }                else if(map[i][j] == 'S')                    sx = i,sy = j;                else if(map[i][j] == 'T')                    ex = i,ey = j;        }//        cout<<len<<endl;        ans = bfs(sx,sy);        printf("%d\n",ans>1000001?-1:ans);    }    return 0;}
阅读全文
0 0
原创粉丝点击