目录
乒乓球[P1042]
题目背景
国际乒联现在主席沙拉拉自从上任以来就立志于推行一系列改革,以推动乒乓球运动在全球的普及。其中11分制改革引起了很大的争议,有一部分球员因为无法适应新规则只能选择退役。华华就是其中一位,他退役之后走上了乒乓球研究工作,意图弄明白11分制和21分制对选手的不同影响。在开展他的研究之前,他首先需要对他多年比赛的统计数据进行一些分析,所以需要你的帮忙。
题目描述
华华通过以下方式进行分析,首先将比赛每个球的胜负列成一张表,然后分别计算在11分制和21分制下,双方的比赛结果(截至记录末尾)。
比如现在有这么一份记录(其中W表示华华获得一分,L表示华华对手获得一分):
WWWWWWWWWWWWWWWWWWWWWWLW
在11分制下,此时比赛的结果是华华第一局11比0获胜,第二局11比0获胜,正在进行第三局,当前比分1比1。而在21分制下,此时比赛结果是华华第一局21比0获胜,正在进行第二局,比分2比1。如果一局比赛刚开始,则此时比分为0比0。直到分差大于或者等于2,才一局结束。
你的程序就是要对于一系列比赛信息的输入(WL形式),输出正确的结果。
输入格式
每个输入文件包含若干行字符串,字符串有大写的W 、L和E组成。其中E表示比赛信息结束,程序应该忽略E之后的所有内容。
输出格式
输出由两部分组成,每部分有若干行,每一行对应一局比赛的比分(按比赛信息输入顺序)。其中第一部分是11分制下的结果,第二部分是21分制下的结果,两部分之间由一个空行分隔。
样例
-
样例输入
WWWWWWWWWWWWWWWWWWWW
WWLWE
-
样例输出
11:0
11:0
1:1
21:0
2:1
提示
每行至多25个字母,最多有2500行。(注:事实上有一个测试点有2501行数据。)
思路:由题意来看,首先输入,然后把输入的数据分给不同的函数处理,最后输出结果即可。首先要注意,当输入E之后,程序忽略所有输入,那么就需要在输入E之后,每次输入但不保存输入数据;同时只需要保存输入数据中的E、L和W。
在处理11分和21分制的函数内部,需要从头开始遍历储存的数据,W和L对应的得分分开储存,并在函数最后输出它们的比值。从题目中21:0这样的比分可以看到,还应该确保双方至少有一方大于对应分制的分数。但是如果存储为空呢?那么比分就是0:0了,因此,当存储字符数组长度为0的时候,就直接输出0:0。
#include<stdio.h>
#include<math.h>
#include<string.h>
void score11(char str[]){
int a = 0,b = 0;
for(int i = 0;str[i]!='\0';i++){
if(str[i]=='W') a++;
else if(str[i]=='L') b++;
if((a>=11||b>=11)&&abs(a - b) > 1){
printf("%d:%d\n",a,b);
a = b = 0;
}
}
if(strlen(str)==0) printf("0:0\n");
else printf("%d:%d\n",a,b);
}
void score21(char str[]){
int a = 0,b = 0;
for(int i = 0;str[i]!='\0';i++){
if(str[i]=='W') a++;
else if(str[i]=='L') b++;
if((a>=21||b>=21)&&abs(a - b) > 1){
printf("%d:%d\n",a,b);
a = b = 0;
}
}
if(strlen(str)==0) printf("0:0\n");
else printf("%d:%d\n",a,b);
}
int main()
{
char str[62505];
int cnt = 0,c,kase = 1;
while(scanf("%c",&c)!=EOF){
if(c!='E'&&(c=='W'||c=='L')&&kase){
str[cnt++] = c;
}
else if(c=='E') kase = 0;
if(!kase) continue;
}
score11(str);
printf("\n");
score21(str);
return 0;
}
扫雷游戏[P2670]
题目描述
扫雷游戏是一款十分经典的单机小游戏。在n行m列的雷区中有一些格子含有地雷(称之为地雷格),其他格子不含地雷(称之为非地雷格)。玩家翻开一个非地雷格时,该格将会出现一个数字——提示周围格子中有多少个是地雷格。游戏的目标是在不翻出任何地雷格的条件下,找出所有的非地雷格。
现在给出n行m列的雷区中的地雷分布,要求计算出每个非地雷格周围的地雷格数。
注:一个格子的周围格子包括其上、下、左、右、左上、右上、左下、右下八个方向上与之直接相邻的格子。
输入格式
第一行是用一个空格隔开的两个整数n和m,分别表示雷区的行数和列数。
接下来n行,每行m个字符,描述了雷区中的地雷分布情况。字符 * 表示相应格子是地雷格,字符 ? 表示相应格子是非地雷格。相邻字符之间无分隔符。
输出格式
输出文件包含n行,每行m个字符,描述整个雷区。用 * 表示地雷格,用周围的地雷个数表示非地雷格。相邻字符之间无分隔符。
样例 #1
-
样例输入 #1
3 3
*??
???
?*?
-
样例输出 #1
*10
221
1*1
样例 #2
-
样例输入 #2
2 3
?*?
*??
-
样例输出 #2
2*1
*21
提示
对于100%的数据,1≤n≤100, 1≤m≤100。
思路:其实就是反向扫雷,给出雷的位置,反过来填数。考虑到越界的问题,所以干脆把m*n的雷区改为(m+2)*(n+2)的雷区,不过并不对最边上的行列进行处理。因此,输入就应该从行1开始而不是行0,从列1开始而不是列0,在行n、列m结束。
如此一来,这个题就比较简单了,把雷区换成二维的字符数组mine[n+2][m+2],输入从mine[1][1]开始到mine[n][m];处理也是从mine[1][1]开始到mine[n][m],每次检测当前位置周围八个位置是否为*,如果是则加一,是?则不处理。最后再输出变化后的mine[1][1]到mine[n][m]即可。
为了第0行和最后一行、第一列和最后一列对数据产生影响,那么可以先把整个mine初始化为某个字符,这里我选择了没有出现过的“!”,用来防止这些无关位置在判断的时候产生影响。
#include<stdio.h>
#include<math.h>
#include<string.h>
int main()
{
int n,m;
scanf("%d%d",&n,&m);
getchar();
char mine[n+2][m+2];
//初始化
for(int i = 0;i<n+2;i++){
for(int j = 0;j<m+2;j++)
mine[i][j] = '!';
}
//输入
for(int i = 1;i<n+1;i++){
for(int j = 1;j<m+1;j++)
scanf("%c",&mine[i][j]);
getchar();
}
//处理
for(int i = 1;i<n+1;i++){
for(int j = 1;j<m+1;j++){
if(mine[i][j]=='?'){
mine[i][j] = '0';
if(mine[i-1][j-1]=='*')
mine[i][j]+=1;
if(mine[i-1][j]=='*')
mine[i][j]+=1;
if(mine[i-1][j+1]=='*')
mine[i][j]+=1;
if(mine[i][j-1]=='*')
mine[i][j]+=1;
if(mine[i][j+1]=='*')
mine[i][j]+=1;
if(mine[i+1][j-1]=='*')
mine[i][j]+=1;
if(mine[i+1][j]=='*')
mine[i][j]+=1;
if(mine[i+1][j+1]=='*')
mine[i][j]+=1;
}
}
}
//输出
for(int i = 1;i<n+1;i++){
for(int j = 1;j<m+1;j++)
printf("%c",mine[i][j]);
printf("\n");
}
return 0;
}
玩具谜题[P1563]
题目描述
小南有一套可爱的玩具小人,它们各有不同的职业。
有一天,这些玩具小人把小南的眼镜藏了起来。小南发现玩具小人们围成了一个圈,它们有的面朝圈内,有的面朝圈外。如下图:
这时 singer 告诉小南一个谜题:“眼镜藏在我左数第3个玩具小人的右数第1个玩具小人的左数第2个玩具小人那里。”
小南发现,这个谜题中玩具小人的朝向非常关键,因为朝内和朝外的玩具小人的左右方向是相反的:面朝圈内的玩具小人,它的左边是顺时针方向,右边是逆时针方向;而面向圈外的玩具小人,它的左边是逆时针方向,右边是顺时针方向。
小南一边艰难地辨认着玩具小人,一边数着:
singer 朝内,左数第3个是 archer。
archer 朝外,右数第1个是 thinker。
thinker 朝外,左数第2个是 writer。
所以眼镜藏在 writer 这里!
虽然成功找回了眼镜,但小南并没有放心。如果下次有更多的玩具小人藏他的眼镜,或是谜题的长度更长,他可能就无法找到眼镜了。所以小南希望你写程序帮他解决类似的谜题。这样的谜題具体可以描述为:
有n个玩具小人围成一圈,已知它们的职业和朝向。现在第 1 个玩具小人告诉小南一个包含 m 条指令的谜題,其中第 z 条指令形如“向左数/右数第 s 个玩具小人。你需要输出依次数完这些指令后,到达的玩具小人的职业。
输入格式
输入的第一行包含两个正整数n,m,表示玩具小人的个数和指令的条数。
接下来 n 行,每行包含一个整数和一个字符串,以逆时针为顺序给出每个玩具小人的朝向和职业。其中 0 表示朝向圈内,1 表示朝向圈外。保证不会出现其他的数。字符串长度不超过 10 且仅由英文字母构成,字符串不为空,并且字符串两两不同。整数和字符串之间用一个空格隔开。
接下来m行,其中第 i 行包含两个整数 a_i,s_i,表示第 i 条指令。若 a_i = 0,表示向左数 s_i 个人;若 a_i = 1,表示向右数 s_i 个人。 保证 a_i 不会出现其他的数,1<= s_i < n。
输出格式
输出一个字符串,表示从第一个读入的小人开始,依次数完m条指令后到达的小人的职业。
样例 #1
-
样例输入 #1
7 3
0 singer
0 reader
0 mengbier
1 thinker
1 archer
0 writer
1 mogician
0 3
1 1
0 2
-
样例输出 #1
writer
样例 #2
-
样例输入 #2
10 10
1 C
0 r
0 P
1 d
1 e
1 m
1 t
1 y
1 u
0 V
1 7
1 1
1 4
0 5
0 3
0 1
1 6
1 2
0 8
0 4
-
样例输出 #2
y
思路:首先输入n行小人的朝向和职业,然后输入m行指令(左右和人数),然后进行数据处理,最后输出。那么在处理的时候需要注意小人的朝向,我们可以假设某个小人朝圈内(0),同时要求向左数(0),这时候是顺时针方向,那么反过来,当小人朝向圈外(1)向右数(1)的时候结果是一样的,因此我们发现:当小人朝向和数数方向的数字相同的时候,是按顺时针方向,不相同则是逆时针方向。
每次开始数都是从第一个小人开始,那么定义一个t = 0,代表眼镜可能在第一个人身上,当朝向数字和方向数字相同的时候,我们让t减去num%n(num代表小人说的人数,n代表总人数,取余是因为绕一圈之后序号没有变换,作差是因为给小人编号是逆时针编号,顺时针就对应了减),不同的话,我们则让t加上num%n。当然,当t减去num%n的时候,t可能小于0,这个时候我们再将其加上一圈的序号(虽然实际上对应的是同一个人,但是却方便了后续的计算和计数);当t加上num%n之后,t可能大于等于n,这个时候只需要减去n,这样就确保了t永远表示在0到n-1之间的小人的编号。
最后,我们只需要输出对应编号为 t 的那个小人的职业就可以。
#include<stdio.h>
#include<math.h>
#include<string.h>
int main()
{
int n,m,d,num,t = 0;
scanf("%d%d",&n,&m);
int direct[n];
char str[n][15];
for(int i = 0;i<n;i++)
scanf("%d %s",&direct[i],str[i]);
for(int i = 0;i<m;i++){
scanf("%d %d",&d,&num);
if(direct[t]==d){
t -= num%n;
if(t<0) t+=n;
}
else if(direct[t]!=d){
t += num%n;
if(t>=n) t-=n;
}
//printf("t:%d %s\n\n\n",t,str[t]);
}
printf("%s",str[t]);
return 0;
}
魔法少女小Scarlet[P4924]
题目描述
Scarlet 最近学会了一个数组魔法,她会在n*n的二维数组上将一个奇数阶方阵按照顺时针或者逆时针旋转 90°。
首先,Scarlet 会把 1 到 n^2 的正整数按照从左往右,从上至下的顺序填入初始的二维数组中,然后她会施放一些简易的魔法。
Scarlet 既不会什么分块特技,也不会什么 Splay 套 Splay,她现在提供给你她的魔法执行顺序,想让你来告诉她魔法按次执行完毕后的二维数组。
输入格式
第一行两个整数 n,m,表示方阵大小和魔法施放次数。
接下来 m 行,每行 4 个整数 x,y,r,z,表示在这次魔法中,Scarlet 会把以第 x 行第 y 列为中心的(2r+1)阶矩阵按照某种时针方向旋转,其中 z = 0 表示顺时针,z = 1 表示逆时针。
输出格式
输出 n 行,每行 n 个用空格隔开的数,表示最终所得的矩阵
样例
-
样例输入
5 4
2 2 1 0
3 3 1 1
4 4 1 0
3 3 2 1
-
样例输出
5 10 3 18 15
4 19 8 17 20
1 14 23 24 25
6 9 2 7 22
11 12 13 16 21
思路:我没有很好的想法,所以只好开(n+1)*(n+1)的数组,这样可以使序号数对应较为方便。
在进行旋转的时候,我的思路是:先确定所旋转的部分的大小和中心,然后开辟另外两个等大的数组temp1和temp2,然后把旋转部分复制到temp1,再对temp1进行旋转,按照对应的坐标变换的关系,把旋转后的结果放进temp2中,然后将temp2替换掉原矩阵中需要旋转的那一部分。
顺时针和逆时针旋转90°的坐标关系可以简单画图自己探索,大抵都是第一行变成最后一列,第一列变成了第一行等等。
#include<stdio.h>
#include<time.h>
#include<string.h>
int main()
{
int m,n,x,y,r,z,p = 1,q = 1;
scanf("%d%d",&n,&m);
int arr[n+1][n+1];
//初始化
for(int i = 1;i<n+1;i++)
for(int j = 1;j<n+1;j++)
arr[i][j] = n * i + j - n;
//操作
for(int i = 0;i<m;i++){
scanf("%d%d%d%d",&x,&y,&r,&z);
int temp1[2*r+2][2*r+2],temp2[2*r+2][2*r+2];
for(int a = 0;a<2*r+2;a++)
for(int b = 0;b<2*r+2;b++)
temp1[a][b] = temp2[a][b]= 0;
//确定旋转部分
for(int a = x-r;a<=x+r;a++)
for(int b = y-r;b<=y+r;b++)
temp1[a-x+r+1][b-y+r+1] = arr[a][b];
if(z==0)//顺时针 temp2[b][n+1-a]
for(int a = 1;a<=2*r+1;a++)
for(int b = 1;b<=2*r+1;b++)
temp2[b][2*r+2-a] = temp1[a][b];
else if(z==1)//逆时针 temp[n+1-b][a]
for(int a = 1;a<=2*r+1;a++)
for(int b = 1;b<=2*r+1;b++)
temp2[2*r+2-b][a] = temp1[a][b];
//替换旋转部分
for(int a = x-r;a<=x+r;a++)
for(int b = y-r;b<=y+r;b++)
arr[a][b] = temp2[a-x+r+1][b-y+r+1];
// printf("\n\n测试:\n");
// for(int i = 1;i<2*r+2;i++){
// for(int j = 1;j<2*r+2;j++){
// if(j==1) printf("%d",temp2[i][j]);
// else printf(" %d",temp2[i][j]);
// }
// printf("\n");
// }
// printf("\n\n");
}
//输出
for(int i = 1;i<n+1;i++){
for(int j = 1;j<n+1;j++){
if(j==1) printf("%d",arr[i][j]);
else printf(" %d",arr[i][j]);
}
printf("\n");
}
return 0;
}
生活大爆炸版石头剪刀布[P1328]
题目描述
石头剪刀布是常见的猜拳游戏:石头胜剪刀,剪刀胜布,布胜石头。如果两个人出拳一样,则不分胜负。在《生活大爆炸》第二季第 8 集中出现了一种石头剪刀布的升级版游戏。
升级版游戏在传统的石头剪刀布游戏的基础上,增加了两个新手势:
斯波克:《星际迷航》主角之一。
蜥蜴人:《星际迷航》中的反面角色。
这五种手势的胜负关系如表一所示,表中列出的是甲对乙的游戏结果。
现在,小 A 和小 B 尝试玩这种升级版的猜拳游戏。已知他们的出拳都是有周期性规律的,但周期长度不一定相等。例如:如果小 A 以 `石头-布-石头-剪刀-蜥蜴人-斯波克` 长度为 6 的周期出拳,那么他的出拳序列就是 `石头-布-石头-剪刀-蜥蜴人-斯波克-石头-布-石头-剪刀-蜥蜴人-斯波克-...`,而如果小 B 以 `剪刀-石头-布-斯波克-蜥蜴人` 长度为 5 的周期出拳,那么他出拳的序列就是 `剪刀-石头-布-斯波克-蜥蜴人-剪刀-石头-布-斯波克-蜥蜴人-...`。
已知小 A 和小 B 一共进行 N 次猜拳。每一次赢的人得 1 分,输的得 0 分;平局两人都得 0 分。现请你统计 N 次猜拳结束之后两人的得分。
输入格式
第一行包含三个整数:N,N_A,N_B,分别表示共进行 N 次猜拳、小 A 出拳的周期长度,小 B 出拳的周期长度。数与数之间以一个空格分隔。
第二行包含 N_A 个整数,表示小 A 出拳的规律,第三行包含 N_B 个整数,表示小 B 出拳的规律。其中,0 表示 `剪刀`,1 表示 `石头`,2 表示 `布`,3 表示 `蜥蜴人`,4 表示 `斯波克`。数与数之间以一个空格分隔。
输出格式
输出一行,包含两个整数,以一个空格分隔,分别表示小 A、小 B 的得分。
样例 #1
-
样例输入 #1
10 5 6
0 1 2 3 4
0 3 4 2 1 0
-
样例输出 #1
6 2
样例 #2
-
样例输入 #2
9 5 5
0 1 2 3 4
1 0 3 2 4
-
样例输出 #2
4 4
思路:不是特别难,只需要知道五种手势之间的关系就可以。然后放进一个大循环里面循环N次 ,每次都需要让A、B的手势序号加一,同时为了不让序号大于等于各自手势周期的时候,只需要每次再取余就可以了。至于比较的部分,要么是AB平局,要么是A获胜,要么是B获胜,所以先找出前两种情况,剩下的都是B获胜。
#include<stdio.h>
#include<time.h>
#include<string.h>
int score_a = 0,score_b = 0;
void judge(int a,int b){
if(a==0&&(b==3||b==2))
score_a++;
else if(a==1&&(b==3||b==0))
score_a++;
else if(a==2&&(b==1||b==4))
score_a++;
else if(a==3&&(b==2||b==4))
score_a++;
else if(a==4&&(b==0||b==1))
score_a++;
else if(a==b);
else score_b++;
//printf("%d %d %d %d\n",a,b,score_a,score_b);
}
int main()
{
int n,t1,t2;
scanf("%d%d%d",&n,&t1,&t2);
int A[t1],B[t2],p,q;
p = q = 0;
for(int i = 0;i<t1;i++)
scanf("%d",&A[i]);
for(int i = 0;i<t2;i++)
scanf("%d",&B[i]);
while(n--){
judge(A[p],B[q]);
p++;q++;
p%=t1;q%=t2;
}
printf("%d %d",score_a,score_b);
return 0;
}
多项式输出[P1067]
题目描述
一元n次多项式可用如下的表达式表示:
其中,a_i x^i 称为 i 次项,a_i 称为 i 次项的系数。给出一个一元多项式各项的次数和系数,请按照如下规定的格式要求输出该多项式:
1. 多项式中自变量为 x,从左到右按照次数递减顺序给出多项式。
2. 多项式中只包含系数不为 0 的项。
3. 如果多项式 n 次项系数为正,则多项式开头不出 `+` 号,如果多项式 n 次项系数为负,则多项式以 `-` 号开头。
4. 对于不是最高次的项,以 `+` 号或者 `-` 号连接此项与前一项,分别表示此项系数为正或者系数为负。紧跟一个正整数,表示此项系数的绝对值(如果一个高于 0 次的项,其系数的绝对值为 1,则无需输出 1)。如果 x 的指数大于 1,则接下来紧跟的指数部分的形式为“x^b”,其中 b 为 x 的指数;如果 x 的指数为 1,则接下来紧跟的指数部分形式为 x;如果 x 的指数为 0,则仅需输出系数即可。
5. 多项式中,多项式的开头、结尾不含多余的空格。
输入格式
输入共有 2 行
第一行 1 个整数,n,表示一元多项式的次数。
第二行有 (n+1) 个整数,其中第 i 个整数表示第 (n-i+1) 次项的系数,每两个整数之间用空格隔开。
输出格式
输出共 1 行,按题目所述格式输出多项式。
样例 #1
-
样例输入 #1
5
100 -1 1 -3 0 10
-
样例输出 #1
100x^5-x^4+x^3-3x^2+10
样例 #2
-
样例输入 #2
3
-50 0 0 1
-
样例输出 #2
-50x^3+1
思路:输入数字N,代表了该多项式的项数为N+1。那么循环N+1次,每次都要搞定其中的第i项。在所有的项中,比较特殊的是第一项,因为它不需要输出正号,并且为0的时候后面的那一项来充当第一项,也不能输出正号,那么优先考虑这种情况。假若第一项不为0,那么它与其他项不同的地方就在于,它不需要输出正号,这也是需要考虑的。
当系数为0的时候,我们可以直接进入下一个循环;如果不为0,那么在kase==0的时候我们可以更改kase的值,用以告诉程序已经有了第一项。(这样在下面的情况中,系数为0的情况已经被讨论过)
当第一项系数不为0的时候(即下面的else if(!kase)),更改kase的值,分三部分来看:符号、系数和它的次数:
对于符号,并不需要过多担心,因为正数的时候不需要输出,只有负数需要,那么我们完全可以直接输出该系数来解决符号问题(1和-1特殊)。
对于系数部分:如果它是最后一项常数项,我们就不需要输出x,而常数项只有第n+1项是,那么我们可以再i==N的时候输出该常数,然后直接进入下一次循环;那么剩下的情况一定不是常数项,并且一定会带有x。这时我们再思考一下正负是否会影响该项的输出?除了1和-1,其他似乎不会影响该项输出,那么我们可以直接输出该项系数。在该情况下,如果为-1,我们只需要输出负号,如果不是-1,那么直接输出该数字。
对于次数:很明显,如果是第n+1项显然系数一定为0,直接跳过,如果不是第n+1项,那么又分成了是否为1次的情况:是则只输出x,不是则输出x^m这样。
当输出了第一项之后,我们就需要处理非首项的情况:我们分为三部分:符号、系数、次数。
对于符号:我们需要分两部分,大于零输出正号,小于零输出负号。
对于系数:只要绝对值不为1,我们可以直接输出它的绝对值。如果绝对值为1,那么就需要考虑是否为常数项:如果是,那么直接输出1,进入下一次循环,不是则不需要处理。
对于次数:如果是常数项,我们直接略过;如果不是,那么我们就需要输出x^m这样的形式。(基于前面的讨论,这里已经不需要考虑系数是否为1或-1:因为当绝对值为1的时候,在输出符号和1之后执行了continue,该语句自然不会被执行到)
#include<stdio.h>
#include<math.h>
#include<string.h>
int main()
{
int n,kase = 0;
scanf("%d",&n);
int arr[n+1];
for(int i = 0;i<n+1;i++)
scanf("%d",&arr[i]);
for(int i = 0;i<n+1;i++){
if(arr[i]==0) continue;
//第一项
else if(!kase){
kase = 1;
//系数
if(n-i==0){
if(abs(arr[i])==1) printf("%d",arr[i]);
continue;
}
if(arr[i]==-1) printf("-");
else if(arr[i]!=1) printf("%d",arr[i]);
//次数
if(n-i==0) continue;
else if(n-i!=0) (n-i==1)?printf("x"):printf("x^%d",n-i);
}
//其他项
else if(kase){
//系数
if(arr[i]>0) printf("+");
else if(arr[i]<0) printf("-");
if(abs(arr[i])!=1) printf("%d",abs(arr[i]));
//次数
if(n-i==0&&abs(arr[i])==1) printf("1");
if(n-i==0) continue;
else if(n-i!=0) (n-i)==1?printf("x"):printf("x^%d",n-i);
}
}
return 0;
}