首页 > 代码库 > UVa 1252 - Twenty Questions(记忆化搜索,状态压缩dp)

UVa 1252 - Twenty Questions(记忆化搜索,状态压缩dp)

题目链接:uva 1252


题意:
有n个长度为m的二进制串,每个都是不同的。
为了把所有字符串区分开,你可以询问,每次可以问某位上是0还是1。
问最少提问次数,可以把所有字符串区分开来。


思路来源于:点击打开链接

思路:
 m很小,可以考虑状态压缩。
dp[s1][s2]表示询问的状态为s1时,此时能猜到状态包含s2时最小需要的步数。
当询问的几位=s2的二进制串小于2时就能区分出来了,dp[s1][s2]=0;
不能区分则再询问一次,s1|=(1<<k),如果问某位为0,则s2不变,问某位为1,则s2|=(1<<k)。
递归下去就能找到答案了。

代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <string>
#include <map>
#include <stack>
#include <vector>
#include <set>
#include <queue>
#define maxn 1005
#define MAXN 1000005
#define mod 100000000
#define INF 0x3f3f3f3f
#define pi acos(-1.0)
#define eps 1e-6
typedef long long ll;
using namespace std;

int n,m,ans,tot,flag;
char s[150][15];
int dp[1<<11][1<<11],mp[150];

int dfs(int s1,int s2)
{
    if(dp[s1][s2]<INF) return dp[s1][s2];
    int i,j,t=0;
    for(i=1;i<=n;i++)
    {
        if((mp[i]&s1)==s2) t++;
    }
    if(t<=1)
    {
        dp[s1][s2]=0;
        return 0;
    }
    int best=INF;
    for(i=0;i<m;i++)
    {
        if(s1&(1<<i)) continue ;
        best=min(best,max(dfs(s1|(1<<i),s2),dfs(s1|(1<<i),s2|(1<<i))));
    }
    dp[s1][s2]=best+1;
    return best+1;
}
int main()
{
    int i,j,t;
    while(~scanf("%d%d",&m,&n))
    {
        if(m==0&&n==0) break ;
        for(i=1;i<=n;i++)
        {
            scanf("%s",s[i]);
            mp[i]=0;
            for(j=0;j<m;j++)
            {
                if(s[i][j]=='1') mp[i]|=(1<<j);
            }
        }
        memset(dp,0x3f,sizeof(dp));
        ans=dfs(0,0);
        printf("%d\n",ans);
    }
    return 0;
}