算法训练 黑白无常 枚举子集

题目链接  

问题描述

  某寝室的同学们在学术完之后准备玩一个游戏:游戏是这样的,每个人头上都被贴了一张白色或者黑色的纸,现在每个人都会说一句话“我看到x张白色纸条和y张黑色的纸条”,又已知每个头上贴着白色纸的人说的是真话、每个头上贴着黑色纸的人说的是谎话,现在要求你判断哪些人头上贴着的是白色的纸条,如果无解输出“NoSolution.”;如果有多组解,则把每个答案中贴白条的人的编号按照大小排列后组成一个数(比如第一个人和第三个人头上贴着的是白纸条,那么这个数就是13;如果第6、7、8个人都贴的是白纸条,那么这个数就是678)输出最小的那个数(如果全部都是黑纸条也满足情况的话,那么输出0)

 分析:需要枚举每种情况,即第i个人是白色还是黑色。需要枚举子集。(0表示黑,1表示白)

用一个数组a[N][2]记录实际每个人说的答案(a[N][0]表示黑,a[N][1]表示白),再用另一个数组表示t[N][2]表示每次枚举情况每个人所能看到的黑白数量。

二进制来表示{0, 1, 2,…,n-1}的子集S:其中从右往左二进制的第i位(从0开始编号)表示元素i是否在集合中(1表示“在”,0表示“不在”)。

#include<iostream>
#include<cstring>
using namespace std;
const int N = 8;
int a[N+2][2], t[N+2][2];
 
int main() {
	int n, b[N+2];//b数组记录枚举的子集情况
	cin>> n;
	for(int i = 0; i < n; i++)  cin>> a[i][1]>> a[i][0];	
	int ans = -1;
	for(int i = 0; i < ( 1 << n); i++){
		memset(t, 0, sizeof(t));
		for(int j = 0; j < n; j++){
			b[j] =(bool) ( i & ( 1 << j));
			for(int k = 0; k < n; k++)
				if( k != j)  t[k][ b[j]]++;
		}
		bool ok = true;
		for(int j = 0; j < n; j++){
			if( b[j] ){  //j说的真话,但是和题目答案不符时false 
				if( t[j][0] != a[j][0] || t[j][1] != a[j][1]){
					ok = false;
					break;
				}
			}else{  //j说的假话,但是和题目答案相符false 
				if( t[j][0] == a[j][0] && t[j][1] == a[j][1]){
					ok = false;
					break;
				}
			}
		}
		if(ok){
			ans = 0;
			for(int j = 0; j < n; j++)
				if( b[j])  ans = ans*10 + j+1;
			break;
		}
	}
	if( ans < 0) cout<< "NoSolution.\n";
	else cout<< ans<< "\n";
	return 0;
}

输入格式

  第一行为一个整数n,接下来n行中的第i行有两个整数x和y,分别表示第i个人说“我看到x张白色纸条和y张黑色的纸条”。

输出格式

  一行。如果无解输出“NoSolution.”。否则输出答案中数值(具体见问题描述)最小的那个,如果全部都是黑纸条也满足情况的话,那么输出0

样例输入

2
1 0
1 0

样例输出

0

样例输入

5
3 1
0 4
1 3
4 0
1 3

样例输出

35

数据规模和约定

  n<=8

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值