问题描述
某寝室的同学们在学术完之后准备玩一个游戏:游戏是这样的,每个人头上都被贴了一张白色或者黑色的纸,现在每个人都会说一句话“我看到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