题意:有n个长m且只含01的字符串,你可以每次询问某位是0还是1,求最少询问辨别任意字符串。
1 1 1 0 先问第一位可以把上三个字符串和下两个字符串区分,对于上面的,问第二位再问第
1 0 1 0 三位可辨别所有字符串,对于下面,问第二位即可区分,综合问三次可区分所有字符串
1 0 0 0
0 1 1 1
0 0 1 1
思路: 设d[ i ][ j ]为已经询问了 i 集合,确认了 j 集合,还剩下询问的最少次数才可以完全辨别任意字符串,如果 s 不是 i 的子集,d[ i ][ j ]=min(d[ i ][ j ] , max(d[ i|s ][ j ]+1,d[ i|s ][ j|s ]+1)),为什么用max,看上面例子理解一下,然后再看代码更容易理解
#include<cstdio> #include<algorithm> using namespace std; const int N=1<<11; const int inf=9999; int vis[N][N],d[N][N],p[130]; int n,m; void init() { for(int i=0;i<(1<<m);i++) for(int j=0;j<n;j++) vis[i][p[j]&i]++; } int dfs(int s,int s0) { if(d[s][s0]!=inf) return d[s][s0]; if(vis[s][s0]<=1) return 0; for(int i=0;i<m;i++) if((s&(1<<i))==0) d[s][s0]=min(d[s][s0],max(dfs(s|(1<<i),s0)+1,dfs(s|(1<<i),s0|(1<<i))+1)); return d[s][s0]; } int main() { char s[14]; while(~scanf("%d%d",&m,&n)&&n&&m) { for(int i=0;i<n;i++) { p[i]=0; scanf("%s",s); for(int j=0;j<m;j++) if(s[j]=='1') p[i]|=(1<<j); } for(int i=0;i<(1<<m);i++) for(int j=0;j<(1<<m);j++) { d[i][j]=inf; vis[i][j]=0; } init(); printf("%d\n",dfs(0,0)); } }