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)