HFOI2017.07.09校内赛(普及组)题解

来源:互联网 发布:react set state 源码 编辑:程序博客网 时间:2024/06/08 18:34
1:月老的苦恼
描述
    学校是一个令月老非常苦恼的地方,因为即便是像信科这样男女比例严重失调的地方,里面所能够产生的缘分数量也是大的惊人的。比如说,30个男生和30个女生,假如他们之间都有产生缘分的可能,那么这里有302 种可能性;月老要从中挑选最好的一种可能性而为他们牵上红线的话,这就够他忙活的了。虽然现实中不一定任意一对男女生之间都有缘分,但是由于人数太多,这无疑会是一项异常繁重的任务。所以月老选修了本学期的程序设计实习课,以期能够通过程序来帮他完成这些复杂工作。然而,由于逃课太多,月老发现自己编的程序实在太挫,只好求助于你来写好这个程序。
    凭借着天赋异能,月老已经摸清楚了信科里有缘分的N个男生和N个女生(各自都从1到N编号)之间的缘分值。在进行牵红线配对工作之前,月老首先想弄清楚的一个重大八卦问题是,这N男N女中,最有女生缘的男生、以及最有男生缘的女生分别是谁。一个男生(女生)的女生缘(男生缘)指的是,他(她)与全部N个女生(男生)的缘分值的总和。
输入
    第一行是一个正整数T(1<=T<=10),表示有多少组测试。
    接下来是T组测试,每组的第一行是一个正整数N(1<=N<=1000),表示男女生各有多少个;紧接着N行每行都有N个小于100的非负整数,第i(1<=i<=N)行的第j(1<=j<=N)个非负整数表示的是编号为i的男生与编号为j的女生之间的缘分值。
输出
    对每组测试,输出一行,包括两个数(空格隔开),分别表示最有女生缘的男生和最有男生缘的女生的编号。若最有女生缘(男生缘)的男生(女生)有多个,则只取其中编号最小的一个输出。
样例输入
    2
    2
    0 1
    2 3
    4
    1 0 1 0
    0 2 0 2
    3 0 3 0
    0 4 0 4
样例输出
    2 2

    4 2

题目要求的就是和最大的行和列。

最暴力的方法是读入数组,然后简单模拟计算,空间复杂度O(n^2),时间复杂度O(n^2)。

代码:

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;const int maxn=1005;int a[maxn][maxn]={},b[maxn]={},c[maxn]={};int main(){int T;scanf("%d",&T);while(T--){memset(a,0,sizeof a);memset(b,0,sizeof b);memset(c,0,sizeof c);int n;scanf("%d",&n);for(int i=0;i<n;i++)for(int j=0;j<n;j++)scanf("%d",&a[i][j]);for(int i=0;i<n;i++)for(int j=0;j<n;j++)b[i]+=a[i][j];for(int i=0;i<n;i++)for(int j=0;j<n;j++)c[i]+=a[j][i];int maxb=0,maxbn=0,maxc=0,maxcn=0;for(int i=0;i<n;i++){if(maxbn<b[i])maxbn=b[i],maxb=i;}for(int i=0;i<n;i++){if(maxcn<c[i])maxcn=c[i],maxc=i;}printf("%d %d\n",maxb+1,maxc+1);}return 0;}

然而,空间复杂度可以降到O(n),具体做法如下:

在读入时计算最有女生缘的男生,并用一维数组保存女生的男生缘,最后处理最有男生缘的女生。

附代码:

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;const int inf=2e9;const int maxn=1005;int a[maxn]={};int main(){int yf,maxb,maxbn,maxc,sum;int T;scanf("%d",&T);while(T--){memset(a,0,sizeof a);maxbn=0;maxc=0;int n;scanf("%d",&n);for(int i=0;i<n;i++){sum=0;for(int j=0;j<n;j++){scanf("%d",&yf);a[j]+=yf;sum+=yf;}if(maxbn<sum)maxbn=sum,maxb=i;}for(int i=1;i<n;i++)if(a[maxc]<a[i])maxc=i;printf("%d %d\n",maxb+1,maxc+1);}return 0;}
2:取石子游戏
描述
    有两堆石子,两个人轮流去取.每次取的时候,只能从较多的那堆石子里取,并且取的数目必须是较少的那堆石子数目的整数倍.最后谁能够把一堆石子取空谁就算赢.
    比如初始的时候两堆石子的数目是25和7
    25 7    -->    11 7    -->    4 7    -->    4 3    -->    1 3    -->    1 0
        选手1取        选手2取        选手1取        选手2取        选手1取
    最后选手1(先取的)获胜,在取的过程中选手2都只有唯一的一种取法。
    给定初始时石子的数目,如果两个人都采取最优策略,请问先手能否获胜。
输入
    输入包含多数数据。每组数据一行,包含两个正整数a和b,表示初始时石子的数目。
    输入以两个0表示结束。
输出
    如果先手胜,输出"win",否则输出"lose"
样例输入
    34 12
    15 24
    0 0
样例输出
    win
    lose
提示
    假设石子数目为(a,b)且a >= b,如果[a/b] >= 2则先手必胜,如果[a/b]<2,那么先手只有唯一的一种取法.
    [a/b]表示a除以b取整后的值.

注意读入时的判断是否结束和清零。

特判a或b和a等于b的情况。

然后按照提示做。

其实是一个递归的过程,但是也可以不用递归做,一个while就搞定了。

最后判断游戏执行的步数(而后到达的玩家胜利)是否归于先走的玩家。

附代码:

#include<cstdio>#include<cmath>using namespace std;int main(){int a,b,t;while(scanf("%d%d",&a,&b)){t=0;if(a==0&&b==0)return 0;if(a==0||b==0){printf("lose\n");continue;}if(a==b){printf("win\n");continue;}if(a<b){int c;c=a;a=b;b=c;}while(1){if(a/b>=2)break;else{t++;int c=a;a=b;b=c-b;}}if(t%2==0)printf("win\n");else printf("lose\n");}}
3:Dungeon Master
描述
    You are trapped in a 3D dungeon and need to find the quickest way out! The dungeon is composed of unit cubes which may or may not be filled with rock. It takes one minute to move one unit north, south, east, west, up or down. You cannot move diagonally and the maze is surrounded by solid rock on all sides.
    Is an escape possible? If yes, how long will it take?
输入
    The input consists of a number of dungeons. Each dungeon description starts with a line containing three integers L, R and C (all limited to 30 in size).
    L is the number of levels making up the dungeon.
    R and C are the number of rows and columns making up the plan of each level.
    Then there will follow L blocks of R lines each containing C characters. Each character describes one cell of the dungeon. A cell full of rock is indicated by a '#' and empty cells are represented by a '.'. Your starting position is indicated by 'S' and the exit by the letter 'E'. There's a single blank line after each level. Input is terminated by three zeroes for L, R and C.
输出
    Each maze generates one line of output. If it is possible to reach the exit, print a line of the form
        Escaped in x minute(s).
    where x is replaced by the shortest time it takes to escape.
    If it is not possible to escape, print the line
        Trapped!
样例输入
    3 4 5
    S....
    .###.
    .##..
    ###.#

    #####
    #####
    ##.##
    ##...

    #####
    #####
    #.###
    ####E

    1 3 3
    S##
    #E#
    ###

    0 0 0
样例输出
    Escaped in 11 minute(s).
    Trapped!

本质上是一个三维的走迷宫问题,而且让人感到非常的简单。幸好没有特殊的格子,一个queue搞定,而且并不需要另开一个bool数组存储是否访问(直接改成‘#’就行了)。请见注释:

代码:

#include<cstdio>#include<cstring>#include<algorithm>#include<queue>using namespace std;const int maxn=35;const int inf=2e9;int l,r,c;char s[maxn][maxn][maxn];//三维数组存储迷宫struct node{int x,y,z,t;};//单个节点信息queue<node>q;int fx,fy,fz,tx,ty,tz;//起始节点和目标节点const int dx[6]={1,-1,0,0,0,0},dy[6]={0,0,1,-1,0,0},dz[6]={0,0,0,0,1,-1};//三维六个方向扩展节点int main(){while(scanf("%d%d%d",&l,&r,&c)&&(l+r+c))//读入判断是否结束{memset(s,'#',sizeof s);bool TF=false;while(!q.empty())q.pop();//这三行都是清零for(int i=1;i<=l;i++)for(int j=1;j<=r;j++)for(int k=1;k<=c;k++){scanf(" %c",&s[i][j][k]);if(s[i][j][k]=='S')fx=i,fy=j,fz=k,s[i][j][k]='.';if(s[i][j][k]=='E')tx=i,ty=j,tz=k,s[i][j][k]='.';}//输入,初始化起始节点和目标节点q.push({fx,fy,fz,0});//加入起始节点while(!q.empty())//开始bfs{node l=q.front();q.pop();//读取最末节点if(l.x==tx&&l.y==ty&&l.z==tz){printf("Escaped in %d minute(s).\n",l.t);TF=true;break;}//判断是否为目标节点for(int i=0;i<6;i++)//扩展节点if(s[l.x+dx[i]][l.y+dy[i]][l.z+dz[i]]=='.')//如果该位置是空的或未访问过{q.push({l.x+dx[i],l.y+dy[i],l.z+dz[i],l.t+1});//插入该节点s[l.x+dx[i]][l.y+dy[i]][l.z+dz[i]]='#';//标记为已经访问}}if(!TF)printf("Trapped!\n");//如果所有节点都搜索完了而没有找到目标节点,输出Trapped!\n}return 0;}


原创粉丝点击