BNUZ-ACM 2018国庆新生欢乐赛部分题解+思路(已解出答案部分)

由于时间不足,本人在新生赛仅浏览了四题,其中两题完全通过。
下面是已解出的一题,供比对和取优。

A. 三角恋

下面贴上原题:
BNUZ-ACM 2018国庆新生欢乐赛部分题解+思路(已解出答案部分)_第1张图片
BNUZ-ACM 2018国庆新生欢乐赛部分题解+思路(已解出答案部分)_第2张图片
我的思路: 首先需要一个while(scanf)循环,以供测试器循环测试各组数据。然后因为需要输入T组数据,所以还需要一个if循环或者while循环。数据的输入也需要利用到到数组,比如a[i]。再者,输出时需要判断是第几次输出,输出一个可自增的量(%d,c++)。最后,需要一个判断if以决定输出嘤嘤嘤还是苦海无涯。

贴上我的代码,我们把它叫做代码a

#include
int main(){
	int a,b=0,c,d,i;
	while(scanf("%d",&a) !=EOF){		
		while(a--){
			int g=0;
			b++;
			scanf("%d",&c);
			int d[c+1];
			for(i=1;i<=c;i++){
				scanf("%d",&d[i]);
			}
				int f=0;
			while(c--){
				f++;
				if(d[d[d[f]]]==f){
					printf("Case #%d: 苦海无涯\n",b);
					g=1;	
					break;
				}	
			}
			if(g==0){
				printf("Case #%d: 嘤嘤嘤\n",b);	
			}
		}
	}
	return 0;
} 

出题者提供的代码,我们把它叫做代码b

#include
int main() {
	int T, n;
	int a[5005];
	scanf("%d", &T);
	int cas = 1;
	while(T--) {
		scanf("%d", &n);
		int flag = 0;
		for(int i = 1; i <= n; i++) {
			scanf("%d", &a[i]);
		}
		for(int i = 1; i <= n; i++) {
			if(a[a[a[i]]] == i){
				flag = 1;
				break;
			}
		}
		if(flag){
			printf("Case #%d: 苦海无涯\n",cas++);
		}else {
			printf("Case #%d: 嘤嘤嘤\n",cas++);
		}
	}
	return 0;
}

在这里对比一下两段代码,以及写一下他们所用到的知识。

输入阶段:

代码a:

	while(scanf("%d",&a) !=EOF){	//输入一个数字,决定总程序要循环多少次
		while(a--){					//while(0)为结束
			int g=0;
			b++;					//b代表是第几次循环,决定输出case几
			scanf("%d",&c);			//输入一个数字代表人数
			int d[c+1];				//定义数组,输入数字的值决定了要定义多少个数组
			for(i = 1; i <= c; i++)
				scanf("%d",&d[i]);	//循环给数组赋值,直到结束
		}
	}

代码b:

	int a[5005];						//直接定义5005个数组
	scanf("%d", &T);
	while(T--) {						//同样循环T次
		scanf("%d", &n);
		int flag = 0;
		for(int i = 1; i <= n; i++) {	//循环赋值
			scanf("%d", &a[i]);
		}

这两段代码的区别在于a代码没有直接定义数组的个数,b直接定义了有5005个数字,根据题目来看,其实两种方法都可以。
while(0)代表while(false)程序将不执行,当while里面的值仍然是非零数的时候,程序会继续循环。

计算判断阶段:

代码a:

		int f=0;					//每次循环先将f定义为0,以方便后面判断
		while(c--){
			f++;					//每次循环自增,判断各人是否符合条件
			if(d[d[d[f]]]==f){
				printf("Case #%d: 苦海无涯\n",b);
				g=1;				//g=1是为了跳过下方嘤嘤嘤的判断
				break;				//跳出语句,不必要重复执行,减少运行时间
			}	
		}
		if(g==0)					//如果不曾有过进入上方的if,则会输出嘤嘤嘤
			printf("Case #%d: 嘤嘤嘤\n",b);	

代码b:

		int cas = 1;
		for(int i = 1; i <= n; i++) {
			if(a[a[a[i]]] == i){
				flag = 1;
				break;
			}
		}
		if(flag){									//if(flag),flag为真时,也就是等于1时进入苦海无涯
			printf("Case #%d: 苦海无涯\n",cas++);	//cas自增,由于两种情况都会输出cas,所以没有问题
		}else {
			printf("Case #%d: 嘤嘤嘤\n",cas++);
		}
	}

在这里讲一下,为什么是a[a[a[i]]] == i呢
我们假设有一个数组是:

1 2 3 4
3 1 2 2

在这个数组里面,很明显是有三角恋的,因为1喜欢3,3喜欢2,2喜欢1。
我们把数组看成是一种函数:

x 1 2 3
f(x) 3 1 2

那么这个表格就可以变成:

x 1 f(f(1)) f(1)
f(x) f(1) f(f(f(1))) f(f(1))

那么问题就变成了f(f(f(1)))是不是等于1的问题了,同样可以推理出可以变成f(f(f(x)))是不是等于x的问题。

代码部分分为两种方式,个人认为第二种方法更好。代码b减少了嵌套,将函数功能模块化,便于维护与理解。同时,有良好的代码习惯作为基础,这些都是我需要学习的。目前来看,我应该在代码的整洁性以及规范性上面下一些功夫。

出题者代码摘自:https://blog.csdn.net/anthony1314/article/details/82926950
感谢bnuz-acm协会为我们举行的国庆欢乐赛,感谢参与了欢乐赛的老师及同学们,希望acm协会能越办越好。

你可能感兴趣的:(整理记录,ACM,BNUZ,C)