有五种花色 外加 五种点数 共25张牌,每次有n张牌,主人知道这n张牌中有哪些牌,并且哪种牌有几张,但是不知道具体是哪张牌,他可以问某种花色,然后知道了哪几张是该花色,也可以问点数,然后就知道了哪几张是这个点数。最终可以把所有牌都确定下来,问最少要询问几次。
这个题目一开始想到枚举(事实证明最后就是枚举),然后又想到二分图去了,。。主要是这个花色对应点数,连成边之后太像二分图了,而且感觉为了得到所有的牌,隐隐约约有点像 DAG上的最小点覆盖。。。而且看了下样例,有点像,所以就去敲二分图去了,结果。。。擦。跪了
其实还是要回到枚举上面来,总共才10个点是不是,用个状态压缩才1000,关键是怎么判断当前枚举是合法的,正向考虑会比较复杂,我想了半天没想到一个比较轻松的方法,但是反向考虑就舒服多了,假设我枚举了k个点,也就是说这k个点(包括花色与点数)都是要询问的,那么不成立的条件有这样:
1.某种牌的两个端点都没询问,而且不止一次(如果只是一次的话,是可以的,因为主人知道所有的牌,这张牌放到最后即可)。
2.某种牌只访问了一个端点,但是这个端点又不止被访问一次(这样的话就肯定无法确定是哪张牌)
所以只要出现了以上两种情况中的一种,即不合法。
#include <iostream> #include <cstdio> #include <cstring> using namespace std; int mat[15][15]; char ch[6]={'A','R','G','Y','W','B'}; int bitcount(int x) { if (x==0) return 0; else return bitcount(x>>1)+(x&1); } bool judge(int x) { int num[20]; memset(num,0,sizeof num); int tot=0; for (int i=1;i<=5;i++) { for (int j=6;j<=10;j++){ if (mat[i][j]){ if (((1<<(i-1))&x) && (!((1<<(j-1))&x))) num[i]++; if ((!((1<<(i-1))&x)) && ((1<<(j-1))&x)) num[j]++; if ((!((1<<(i-1))&x)) && (!((1<<(j-1))&x))) tot++; } } } //cout<<tot<<endl; if (tot>1) return 0; for (int i=1;i<=10;i++){ if (num[i]>1) return 0; } return 1; } int main() { int n; char s[3]; while (scanf("%d",&n)!=EOF) { memset(mat,0,sizeof mat); for (int i=0;i<n;i++){ scanf("%s",s); int a=0; for (int i=1;i<=5;i++){ if (s[0]==ch[i]){a=i;break;} } int b=s[1]-'0'+5; mat[a][b]=1; // cout<<a<<" "<<b<<endl; } int ans=100; for (int i=0;i<(1<<11);i++){ if (judge(i)){ ans=min(ans,bitcount(i)); } //cout<<ans<<endl; //if (ans==0) break; } printf("%d ",ans); } return 0; }