2019BNUZ_ACM国庆欢乐赛题解

目录

  • 题目来源 :
    • A.小沛的暑假安排
    • B.nsy上幼儿园
    • C.nsy上小学
    • D.怡姐的游戏
    • E.如风般奔跑
    • F.cjb找朋友
    • G. 黑魔仙wgc的攻击
    • H. 皓洲喜欢经纬线
    • I. 小枫买车
    • J. 单身狗的乐趣
    • K. YXL的象棋游戏
    • L. 泡面


题目来源 :

2019BNUZ_ACM国庆欢乐赛


A.小沛的暑假安排

Solution

​这是一道没什么难度主要是有点麻烦的题目。

首先7月和8月共有62天,即建立一个大小为62的数组,即每一天都对应数组里第一个位置,数组里默认的值为零,在输入日期后,把日期对应的数组的位置都加一,当所有日期输入完后,遍历一遍数组,数组里有多少个0便有多少空闲日子,数组里有多少大于1的值,便为有冲突的日子。

AC_Code

#include 
int main() {
	int T,N;
	int D[63]= {0};
	int a1,a2,b1,b2;
	int sum1=0,sum2=0;
	scanf("%d",&T);
	for(int i=0; i<T; i++) {
		scanf("%d",&N);
		for(int i=0; i<63; i++) {
			D[i] = 0;
		}
		for(int j=0; j<N; j++) {
			scanf("%d %d %d %d",&a1,&a2,&b1,&b2);
			if(a1==7&b1==7) {
				for(int k=a2; k<=b2; k++)
					D[k]++;
			}
			if(a1==7&b1==8) {
				for(int k=a2; k<=b2+31; k++)
					D[k]++;
			}
			if(a1==8&b1==8) {
				for(int k=a2+31; k<=b2+31; k++)
					D[k]++;
			}
		}
		for(int k=1; k<63; k++) {
			if(D[k]==0)
				sum1++;
			if(D[k]>1)
				sum2++;
		}
		printf("Case #%d:\n%d %d\n",i+1,sum2,sum1);
		sum1=0;
		sum2=0;
	}
	return 0;
}


B.nsy上幼儿园

Solution

​把所有的字母进行排序(题解使用冒泡排序),然后判断全部字母是否符合:后一个字母比前一个字母多1(因为在ASCII码中,所有字母是连在一起的).
2019BNUZ_ACM国庆欢乐赛题解_第1张图片

AC_Code

#include
#include
int main() {
	int n;
	scanf("%d",&n);
	int cas = 1;
	while(n--) {
		char ch[105];
		scanf("%s",ch);
		int len = strlen(ch);
		for(int i=0; i<len-1; i++) {
			for(int j=0; j<len-i-1; j++) {
				if(ch[j] > ch[j+1]) {
					char temp = ch[j];
					ch[j] = ch[j+1];
					ch[j+1] = temp;
				}
			}
		}
		int flag = 1;
		for(int i=1; i<len; i++) {
			if(ch[i] - ch[i-1] != 1) {
				flag = 0;
			}
		}
		printf("Case #%d:\n",cas++);
		if(flag == 1) {
			printf("Yes\n");
		}
        else {
			printf("No\n");
		}
	}
	return 0;
}


C.nsy上小学

Solution

​究极签到题,a,b,c的数据范围很大,开long long,直接加起来就可以了。

AC_Code

#include
int main() {
	int t, cas = 0;
	long long a, b, c;
	scanf("%d",&t);
	while(t--) {
		scanf("%lld%lld%lld",&a,&b,&c);
		printf("Case #%d:\n%lld\n",++cas,a+b+c);
	}
    return 0;
}


D.怡姐的游戏

Solution

​这是一道水题,出这道题主要是想让大家了解一下ASCII表,将字符转化为对应的数字,题目本身不难,只需要遍历一遍每个字符,将他们对应的数字相乘再取模57看看相不相等即可。

AC_Code

#include
int main() {
	char a[6]= {'0'},b[10]= {'0'};
	int T;
	scanf("%d",&T);
	for(int j=1; j<=T; j++) {
		scanf("%s",a);
		scanf("%s",b);
		int ans1 = 1, ans2 = 1;
		int i = 0;
		while(a[i]>=65 && a[i]<=90) {
			ans1 *= (a[i]-64);
			i++;
		}
		i=0;
		while(b[i]>=65 && b[i]<=90) {
			ans2 *= (b[i]-64);
			i++;
		}
		if(ans1%57 == ans2%57)
            printf("Case #%d:\nYes\n",j);
		else
            printf("Case #%d:\nNo\n",j);
	}
	return 0;
}


E.如风般奔跑

Solution

​这题题目的大意是:给你一个800米的环形跑道以及小陈和小张的速度,问你什么时候他们能在200米处相遇。

​这题十分简单,题目中最重要的关键句就是时间只能以秒递进。根据数学计算可知,小陈与小张若要在200米处相遇,那么他们相遇的时间必在800秒以内,因为800秒之后他们两个人将会回到跑道的原点,相当于重新开始,所以问题就转化成了:在800秒内,有没有可能小张与小陈相遇。

​首先,时间只能以秒递进,我们就从1-800 for循环一遍,寻找是否有解就好了。

​然后,题目中有提到他需要跑完那圈再退出比赛,也就是剩下的路程600处以小陈的速度就好了,不过时间以秒递进,所以如果有小数位的需要向上取整
2019BNUZ_ACM国庆欢乐赛题解_第2张图片

600s + (( 800m - 200m ) / 3(m/s) ) = 600s + 200s = 800s

给新生的话:

这题的别名叫舔狗的辛酸,怎么没人写啊啊啊啊啊,这么简单,这题不卡时间,没有特判,思路对了就能过了,不会向上取整+0.9999转int也可以呀,或者mod不等于0自增就好了。

​在思考可能有人看不懂样例,所以po了一下样例解析。

​本来思考了一下会不会有点简单,可以把跑道和喝水点也改成输入值 (那样就会很快乐) ,后来想了想还是弄道签到题吧,结果前两天都没多少人写T_T

​那么,很感谢那些写了或者看过这题的朋友,也感谢来参与比赛的你们,希望ACM协会能越办越好。

AC_Code

#include
int main() {
	int T;
	while(scanf("%d",&T)!=EOF) {
		for(int o=0; o<T; o++) {
			int n,m;
			scanf("%d %d",&n,&m);
			int a,b;
			printf("Case #%d:\n",o+1);
			int ans=1;//用于记录是否有输出
			for(int ii=1; ii<=800; ii++) { //时间1-800
				a=n*ii%800;//计算位置
				b=m*ii%800;
				if(a==b&&a==200) { //判断条件,相遇且在200米处
					ans=0;
					int mod = 0;//用于计算是否需要多跑一秒
					if(600%n!=0)
						mod++;
					printf("%d\n",ii+600/n+mod);
					break;
				}
			}
			if(ans)
				printf("我太难了\n");
		}
	}
	return 0;
}


F.cjb找朋友

Solution

​找规律,题目又臭又长,但其实如果你读懂了就会发现,其实就是一个经过一点处理的斐波那契数列题目。

斐波那契数列指的是这样一个数列 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233,377,610,987,1597,2584,4181,6765,10946,17711,28657,46368…

​这个数列从第3项开始,每一项都等于前两项之和。

​因为每次都是前一天没去的人加上前一天新找到的人,第n-1天新找到的人和去找的人数量相同,第n-1天没去的人即为第n-2天去了的人。

​规律用代码表达为 f(n) = f(n-1) + f(n-2),用res表示第n天上网的人数则:res = f(n)

​因为cjb在自家网吧上网不用付钱,并且根据规律他只在奇数的天去上网,但是他当天不需要给钱不影响后续的规律,所以当n为奇数时,需要在结算的时候进行一次计算 res = f(n)-1。

​最后将res×7即为网费,需要注意当n>83的时候,已经超过了1e9+7,所以需要特判,注意数据范围。

​题目测试数据较少,不需要使用矩阵快速幂(有大佬用了这个)或者离线预处理(指在输入前把所有可能用到的答案全部计算完,输入后直接输出对应答案即可)。

AC_Code

#include
int main() {
	int t, n, cas = 0;
	scanf("%d",&t);
	while(t--) {
		scanf("%d",&n);
		printf("Case #%d:\n",++cas);
		if(n > 83) {
			printf("zhipiao\n");
		}
		else {
			long long pre = 1, now = 1, later = 2;
			for(int i=2; i<n; i++) {
				later = pre + now;
				pre = now;
				now = later;
			}
			if(n % 2 == 1) {
				now--;
			}
			long long ans = now * 7;
			printf("%lld\n",ans);
		}
	}
	return 0;
}


G. 黑魔仙wgc的攻击

Solution

这道题符合题意有两个条件:

  1. 是否为c的倍数;

  2. 是否包含c这个数字

第一个条件很简单,直接用一个数去mod c,判断是否等于0就可以了。

第二个条件用一个while循环,将要判断的数字一个一个拆开,若出现c这个数字直接跳出循环。

本题的难点在于c的取值范围,c的取值范围是0-9,也就是说可以取0这个值,0的倍数都是0,所以当c=0的时候,只需要判断第二个条件即可,若c=0时,拿一个数去mod 0,就会出现运行时错误。

AC_Code

#include
int main() {
	int t,a,b,c,ans;
	while(~scanf("%d",&t)) {
		for(int z = 1; z <= t; z++) {
			scanf("%d%d%d",&a,&b,&c);
			ans = 0;
			for(int i = a; i <= b; i++) {
				if(c != 0) {
					if(i % c == 0)
						ans++;
					else {
						int num = i;
						while(num) {
							if(num % 10 == c) {
								ans++;
								break;
							}
							num /= 10;
						}
					}
				}
                else {
					int num = i;
					while(num) {
						if(num % 10 == c) {
							ans++;
							break;
						}
						num /= 10;
					}
				}
			}
			printf("Case #%d:\n%d\n",z,ans);
		}
	}
	return 0; 
}


H. 皓洲喜欢经纬线

Solution

题意很直白,输入一个整数n(-1000~1000)字符c(N、S、E、W)表示经纬线。要求判断所在的时区。

2019BNUZ_ACM国庆欢乐赛题解_第3张图片

说明一下:

-100 E就是向东旋转-100度,也就是向西旋转100度。

190 E就是180 E多转了10度,180 E和 180 W是重合的,所以180 E多转10度就是170 W。

370 E就是10 E多转了一圈,结果还是10 E。

具体解法如下:

1、首先判断南北纬(N、S)直接输出NO。

2、因为数据范围是(-1000 ~ 1000)的整数,所以需要先把数据转化为0~180的整数。

第一步:如果n>360度就一直-360度(或直接模360),如果n<360就一直+360度 (或把负号改成正号,W和E互换,然后再模360)。这样数据就是0~360了。

第二步:如果n大于180度,用360-n表示,然后东西经再进行调换(W和E互换)。这样数据就是0~180了。

3、时区算法:经度数除以15得数就是时区。如果有余数,余数大于7.5 就加一个时区。

4、特判一下UTC和UTC+12的情况。

AC_Code

#include
int main()
{
	int T,cas = 1;
	scanf("%d",&T);
	while(T--)
	{
		double n;
		char c;
		int flag = -1;//判断东西经,0表示东经,1表示西经
		scanf("%lf %c",&n,&c);
		if(c == 'E')
			 flag = 0;
		else if(c == 'W')
			 flag = 1;
		else//南北纬直接输出NO 
		{
			printf("Case #%d:\nNO\n",cas++);
			continue;
		}
		while(n>360)//大于360度减去360度 
			n -= 360;
		while(n<0)//小于0度加上360度 
			n += 360;
		if(n>180)//大于180度,用360-n表示,东西经再进行调换 
		{
			n = 360-n;
			flag = !flag;
		}
		//时区算法:经度数除以15得数就是时区。如果有余数,余数大于7.5 就加一个时区。 
		int ans = n/15;
		if(n-ans*15>7.5)
			ans++;
		//特判一下两种情况 
		if(ans == 0)
			printf("Case #%d:\nUTC\n",cas++);
		else if(ans == 12)
			printf("Case #%d:\nUTC+12\n",cas++);
		else if(flag)
			printf("Case #%d:\nUTC-%d\n",cas++,ans);
		else
			printf("Case #%d:\nUTC+%d\n",cas++,ans);
	}
	return 0;	
} 


I. 小枫买车

Solution

题意很简单,经典的换门问题,求老板刮开奖券之后小枫换一张奖券可以中奖的概率。

分情况讨论:

  • 一开始抽到有奖奖券的概率:b / ( a + b)

    • 换奖券中奖的概率:(b - 1) / (a + b- c - 1)
  • 一开始没有抽到奖的概率:a / (a + b)

    • 换奖券中奖的概率:b / (a + b - c - 1)

相乘再相加即可得(a * b + b * (b - 1) ) / ( (a + b) * (a + b - c - 1) )

AC_Code

#include
int main() {
	int T,cas = 1;
	scanf("%d",&T);
	while(T--) {
		double a,b,c;
		scanf("%lf %lf %lf",&a,&b,&c);
		printf("Case #%d:\n%.2f%%\n",cas++,b/(a+b)*(b-1)/(a+b-c-1)*100 + a/(a+b)*b/(a+b-c-1)*100);
	}
	return 0;
}


J. 单身狗的乐趣

Solution

​题目意思等同于:长度为n (n < 105)的字符串,字符串仅包含’0’-‘9’。把这个字符串切割成若干个子串,每个子串作为一个十进制的数,能被3整除,且不含前导0。求有多少种切割方案(答案模998244353).

​我们需要知道多位数能被3整除的特性:每一位加起来的和能被3整除。

​如162可被3整除:1 + 6 + 2 = 9,9 % 3 = 0.

​因此我们从前往后遍历,如果从开头到当前位置每一位的和能整除3则说明当前为可切割点。因为不含前导零所以需要考虑当前位置是否为0,如果不为0则可以被作为下一次的切割点。如果能作为切割点,则更新当前能被切割的最大方案数,否则沿用上一次可切割点的最大方案数。

AC_Code

#include
int main() {
	int t, n, cas = 0;
	scanf("%d",&t);
	while(t--) {
		char str[100000+5];
		scanf("%d%s",&n,str);
		long long sum = 0, ans = 1, dp = 0, mod = 998244353;
		for(int i=0; i<n; i++) {
			sum = (sum + str[i] - '0') % 3;
			if(sum == 0) {
				ans = (ans + dp) % mod;
				if(str[i] != '0')
					dp = ans;
			}
		}
		printf("Case #%d:\n",++cas);
		if(sum==0)
			printf("%d\n",ans);
		else
			printf("0\n");
	}
	return 0;
}


K. YXL的象棋游戏

Solution

​博弈题,因为每个点周围的日字型是不能下的,所以同一行或者同一列是不受影响的。

​如图所示,红色为下的点,紫色为受到红色影响所以不能下的点。

2019BNUZ_ACM国庆欢乐赛题解_第4张图片

​其实想让对方输非常简单,如果棋子能下的位置数量为偶数,即(n+1)*(m+1)为偶数时,你只要把棋子下在对方的轴对称点即可。​如果对方把棋子下在红色点,我们可以选定本局的绿色轴对称下在绿色点或者选择蓝色轴对称下在蓝色点。然后后面的点也根据这条轴去下对方的对称点。
​如图所示:

2019BNUZ_ACM国庆欢乐赛题解_第5张图片
如果棋子能下的位置为奇数,你只要把棋子下在对方的中心对称点即可。但是中心点没有对称点。

我们把下对方对称点的行为叫做针对。

​那么如果(n+1)×(m+1)为偶数的话,则所有点都会有对称点,那么先手者被针对会输掉比赛。

​但是如果(n+1)×(m+1)为奇数的话,则中心点没有对称点,那么先手下中心点,后手者则只能被针对。

AC_Code

#include
int main() {
	long long t,cas = 0;
	scanf("%lld",&t);
	while(t--) {
		long long n,m;
		scanf("%lld%lld",&n,&m);
		printf("Case #%lld:\n",++cas);
		if(((n+1)*(m+1)) % 2 == 1) {
			printf("1\n");
		}
		else {
			printf("0\n");
		}
	}
    return 0;
}


L. 泡面

Solution

​有一堆木板,问能组合出多高的梯子,能否翻过墙壁。

​根据梯子的组合规则可以知道,利用现有的木板所能搭出最高的梯子,取决于次最长的那一块木板。

​至于能不能搭出这么高的梯子,又取决于除去作为边的2块木板后,剩余木板的数量。

​所以我们只要取出次长的木板,判断最高能搭多高的梯子。然后判断剩余木板的数量足够搭出多高的梯子,最后判断一下搭出的梯子能否翻过墙壁即可。

​当然,需要特别判断一下,如果墙的高度为0说明不存在墙,那么无论怎样都可以翻过墙了。

AC_Code

#include
int main() {
	int t;
	scanf("%d",&t);
	for(int cas = 1; cas <= t; cas++) {
		int n, h;
		scanf("%d %d",&n,&h);
		//输入数据
		//数据已经是升序排列的了,所以不需要排序
		int data[1000];
		for(int i = 0; i < n; i++)
			scanf("%d",&data[i]);
		//k表示能组出多少步的梯子
		int k = 0;
		//因为只有木板总数量大于2才能组出梯子
		if(n > 2) {
			//data[n-2] 是第二长的木板
			//n-2 即为"除去作为边的 2 块木板后,剩余木板的数量"
			//如果剩余木板数量足够搭出最高梯子
			if((data[n-2] - 1) >= (n-2))
				k = n-2;
			//如果剩余木板数量不够搭出最高梯子,那么有多少木板就搭多高的梯子
			else
				k = data[n-2] - 1;
		}
		//特殊判断一下当墙的高度为0的情况,也能直接翻过墙
		if(h == 0 || (k+1 >= h))
			printf("Case #%d:\nYES\n", cas);
		else
			printf("Case #%d:\nNO\n", cas);
	}
	return 0;
}

你可能感兴趣的:(补题库)