猜对了一半 (20分)

猜对了一半 (20分)

赛场内 n (0<n≤10) 名运动员正在参加百米短跑比赛。赛场外有 m (0<m≤100) 名热心观众,他们每人都对比赛结果作出了 2 个预测。比赛结束后,运动员的名次各不相同,但令人惊奇的是每位观众都猜对了一半。请问运动员取得的实际名次是多少?

例如场内有 4 名运动员参加比赛,场外 3 名观众的预测分别为:

1 号队员第 1,2 号队员第 3
3 号队员第 1,4 号队员第 4
4 号队员第 2,1 号队员第 3
根据每人猜对一半因此可以推理得:

1 号队员第 4
2 号队员第 3
3 号队员第 1
4 号队员第 2
请编写程序,根据观众的预测来推算运动员的实际名次。

输入格式

正整数 n
正整数 m
m 行数据,为 m 位观众的预测
其中每行包含 4 个整数:x1 r1 x2 r2
它们分别表示观众的两个预测:x1号运动员第 r1,x2号运动员第 r2

输出格式

若问题无解,则输出 None
若问题有解,则输出多行数据,每一行表示一个答案,按字典序输出。
每一行包含 n 个整数,分别是 1 ~ n 号运动员取得的实际名次。

注:整数间用空格间隔,行末没有空格。

输入样例1

4
3
1 1 2 3
3 1 4 4
4 2 1 3

输出样例1

4 3 1 2

输入样例2

4
3
3 4 2 1
4 3 3 2
1 4 2 3

输出样例2

None

输入样例3

4
3
2 4 4 1
4 2 2 3
3 4 1 1

输出样例3

1 4 3 2
2 3 4 1

【思路】
题中每位观众都猜对了一半,即可把该观众的两个预测分成两种情况讨论。假设观众的第一个预测为真,则第二个预测为假,或者假设观众的第一个预测为假,则第二个预测为真。(预测为真即是运动员的名次为真,预测为假即是运动员的名次为假)
同一个运动员只能有一个名次,同一个名次只能有一个运动员,假设观众预测某一运动员的名次为假时,之后的观众预测该运动员的名次就不能为真,否则两则相矛盾。若观众预测运动员的名次少于所给运动员的个数时,则要补齐剩余运动员的名次。不同观众预测同一个运动员的假名次,可能有多个。若观众预测运动员的名次少于所给运动员的个数时,则要补齐剩余运动员的名次。

代码:

#include<stdio.h>
/*n运动员人数,m观众人数,c所求运动员名次的方案数,
falg[]标记观众预测为真的名次,a[][]保存观众的预测*/
int n, m, c, flag[11], a[105][6];

//mc运动员名次,k观众猜测此运动员假名次个数,jiamc[]保存观众猜测此运动员名次为假的名次 
struct{
	int mc, k, jiamc[105];
}ydy[11]; 

//per[]为每个方案运动员的名次,sum保存每个方案运动员名次的十进制整数 
struct{
	int per[11];
	long long sum;
} ans[1000], t;

int pj(int n, int mc){//判断n号运动员,mc是否为假 
	int i;
	
	for(i = 0; i < ydy[n].k; i++){
		if(ydy[n].jiamc[i] == mc){
			return 0;//mc为假,返回0 
		}
	}
	
	return 1;//mc为真,返回1 
}

int shu(int cur){//回溯法把剩余没有名次的运动员根据约束条件补齐 
	if(cur > n){
	    int i;
	    
	    for(i = 1; i <= n; i++){
	        ans[c].per[i] = ydy[i].mc;
		}
		
		c++;
	}
	else{
		int i;
		
		if(ydy[cur].mc == 0){//运动员cur没有名次 
			for(i = 1; i <= n; i++){//运动员名次 
				if(flag[i] == 0 && pj(cur, i)){//名次i未被标记且第cur个运动员的名次i不在假名次中 
					ydy[cur].mc = i;
					flag[i] = 1;
					shu(cur + 1);
					ydy[cur].mc = 0;
					flag[i] = 0;
				}
			}
		}
		else{//运动员cur有名次 
			shu(cur + 1);
		}
	}
}

int search(int cur){
	if(cur < m){
	    int i;
		
		for(i = 0; i < 2; i++){/*i == 0时,假设观众cur第一个预测为真,则其第二个预测为假,
		i == 1时,假设观众cur第二个预测为真,则其第一个预测为假 */
			
			if(ydy[a[cur][2 * i + 1]].mc == a[cur][2 * i + 2]){/*观众cur预测同一个运动员的名次
			与之前观众预测相同*/ 

			    if(pj(a[cur][3 - 2 * i], a[cur][4 - 2 * i])){//观众cur预测的假名次,还未保存在在该运动员的jiamc[]中 
			    	ydy[a[cur][3 - 2 * i]].jiamc[ydy[a[cur][3 - 2 * i]].k] = a[cur][4 - 2 * i];//保存相应运动员名次为假的名次 
			    	ydy[a[cur][3 - 2 * i]].k++;//假名次个数增加 
			    	    
			    	search(cur + 1);
			    	
			    	ydy[a[cur][3 - 2 * i]].jiamc[ydy[a[cur][3 - 2 * i]].k] = 0;
			    	ydy[a[cur][3 - 2 * i]].k--;
				} 
			    	
			   	else{//观众cur预测的假名次,已经保存在该运动员的jiamc[]中
			    	search(cur + 1);
				}
			}
			else{//观众cur预测不是同一个运动员 
			    //名次未被标记且运动员名次不相同且运动员的jiamc[]等于1,即运动员名次为真,或者第一层 
				if((flag[a[cur][2 * i + 2]] == 0 && ydy[a[cur][2 * i + 1]].mc == 0 && pj(a[cur][2 * i + 1], a[cur][2 * i + 2])) || cur == 0){
					ydy[a[cur][2 * i + 1]].mc = a[cur][2 * i + 2];//保存相应运动员的名次 
					flag[a[cur][2 * i + 2]] = 1;//标记名次 
					ydy[a[cur][3 - 2 * i]].jiamc[ydy[a[cur][3 - 2 * i]].k] = a[cur][4 - 2 * i];//保存相应运动员名次为假的名次
					ydy[a[cur][3 - 2 * i]].k++;//假名次个数增加 
						
					search(cur + 1);
					
					ydy[a[cur][2 * i + 1]].mc = 0;	
					flag[a[cur][2 * i + 2]] = 0;
					ydy[a[cur][3 - 2 * i]].jiamc[ydy[a[cur][3 - 2 * i]].k] = 0;
					ydy[a[cur][3 - 2 * i]].k--;				   	
				}
			}
			
		}
			
	}
	
	else if(cur == m){//cur等于观众人数,进入shu()补齐其余运动员的名次 
		shu(1);
	}
}


int main()
{
	int i, j, k;
	
	scanf("%d", &n);
	
	scanf("%d", &m);
	
	for(i = 0; i < m; i++){
		scanf("%d %d %d %d", &a[i][1], &a[i][2], &a[i][3], &a[i][4]);
	}
	
	search(0);
	
	if(c == 0){//方案数为0 
		printf("None\n");
		return 0;
	}
	
	for(i = 0; i < c; i++){//把每一行运动员名次转换成十进制整数 
		for(j = 1; j <= n; j++){
			ans[i].sum = ans[i].sum * 10 + ans[i].per[j];
		}
	}
	
	for(i = 0; i < c - 1; i++){//通过上面得到的整数从小到大排序,即可得到字典序 
		k = i;
		for(j = k + 1; j < c; j++){
			if(ans[j].sum < ans[k].sum){
				k = j;
			}
		}
		t = ans[k];
		ans[k] = ans[i];
		ans[i] = t;
	}
	
	for(i = 0; i < c; i++){//输出答案 
		for(j = 1; j <= n; j++){
			if(j == 1){
				printf("%d", ans[i].per[j]);
			}
			else{
				printf(" %d", ans[i].per[j]);
			}
		}
		
		printf("\n");
	}
	
	return 0;
}

虽然我给出的程序可以直接通过全部测试,但限于本人能力,不一定是最完美的解决方案。

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值