Twenty Questions UVA - 1252
题目大意:
有n个01串,长度为m。可以询问某一位,将串相互区别。问在最优方案下,至多要问几次,才能将某一个串区别出来
举个例子:
1000
0011
0010
1011
区别出第一个串,只需要问第一位;
区别出第二个串或第三个串,只需要问后两位;
区别出第四个串,也只需要问两位
因此答案是2
思路:
考虑一个询问序列,如果能区别出一个串出来,就是合法的
可光有询问序列不好办啊,要有具体的询问结果啊,那就表示出来呗
对每个询问,就要考虑不同的结果,就好了
就能dp了,状态f(s,s0),分别表示出询问序列,和结果为1的位
s0是s的子集
就有f[s][s0]=min(f[s][s0],1+max( f(s^t,s0) , f(s^t,s0^t) )),t为新考虑的一位
结果就是f(0,0)
#include<iostream> #include<cstdio> #include<cstring> using namespace std; const int OM=1<<11,N=128+3; const int INF=0x7f7f7f7f; #define ll long long int m,n,all; ll a[N]; int dp[OM][OM]; bool belong(int a,int b) { return (a&b)==a; } bool judge(int s,int s0) { int cnt=0; for(int i=1;i<=n;i++) //if((s&a[i])==s0) if( belong(s0,a[i])&& belong( s^s0,~a[i] ) ) cnt++; return cnt<=1; } int dps(int s,int s0) { if(dp[s][s0]!=-1) return dp[s][s0]; if(judge(s,s0)) return dp[s][s0]=0; dp[s][s0]=INF; for(int t=1 ; t<all ; t<<=1) if(!(s&t)) dp[s][s0]=min(dp[s][s0],1+max( dps(s^t,s0) , dps(s^t,s0^t) )); return dp[s][s0]; } int binar(ll x) { int ret=0; for(int i=1;i<=m;i++) { ret<<=1; if(x%10)ret^=1; x/=10; } return ret; } int main() { while(scanf("%d%d",&m,&n)==2 && m+n) { all=1<<m; for(int i=1;i<=n;i++) { scanf("%lld",&a[i]); a[i]=binar(a[i]); } memset(dp,0xff,sizeof(dp)); printf("%d\n",dps(0,0)); } return 0; }