首页 > 代码库 > UVa 1252 (状压DP + 记忆化搜索) Twenty Questions

UVa 1252 (状压DP + 记忆化搜索) Twenty Questions

题意:

有n个长为m的各不相同的二进制数(允许存在前导0),别人已经事先想好n个数中的一个数W,你要猜出这个数。

每次只可以询问该数的第K为是否为1.

问采用最优询问策略,则最少需要询问多少次能保证猜到。

比如有1100 和 0110两个数,只需要询问第一或第三位数是否为1,即可猜中,因此答案为1.

分析:

d(s, a)表示已经询问了的集合s,在已经询问了的集合中W中为1的集合为a,还需要询问多少次。

如果下一次询问第k位,则询问次数为:

  

然后取所有k里的最小值即可。

 

预处理:

对于每个s和a,可以先把满足条件的数的个数记录下来,保存在cnt[s][a]里。

 

 1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 using namespace std; 5  6 const int maxm = 12, maxn = 130; 7 char object[maxn][maxm + 10]; 8 int d[1<<maxm][1<<maxm], cnt[1<<maxm][1<<maxm], vis[1<<maxm][1<<maxm]; 9 int m, n, kase = 0;10 11 int dp(int s, int a)12 {13     if(cnt[s][a] <= 1)  return 0;14     if(cnt[s][a] == 2)  return 1;15 16     int& ans = d[s][a];17     if(vis[s][a] == kase)  return ans;18     vis[s][a] = kase;19 20     ans = m;21     for(int k = 0; k < m; ++k)22     {23         if(!(s&(1<<k)))24         {25             int s2 = s | (1<<k), a2 = a | (1<<k);26             if(cnt[s2][a] >= 1 && cnt[s2][a2] >= 1)27             {28                 int need = max(dp(s2, a2), dp(s2, a)) + 1;29                 ans = min(ans, need);30             }31         }32     }33     return ans;34 }35 36 int main()37 {38     //freopen("in.txt", "r", stdin);39     while(scanf("%d%d", &m, &n) && n)40     {41         ++kase;42         for(int i = 0; i < n; ++i)43             scanf("%s", object[i]);44         memset(vis, 0, sizeof(vis));45         memset(cnt, 0, sizeof(cnt));46         for(int i = 0; i < n; ++i)47         {48             int feature = 0;49             for(int f = 0; f < m; ++f)50                 if(object[i][f] == 1) feature |= (1 << f);51             for(int s = 0; s < (1<<m); ++s)52                 cnt[s][s&feature]++;53         }54         printf("%d\n", dp(0, 0));55     }56 57     return 0;58 }
代码君

 

UVa 1252 (状压DP + 记忆化搜索) Twenty Questions