UVA 1252 Twenty Questions

来源:互联网 发布:actor 知乎 编辑:程序博客网 时间:2024/05/17 02:36

状态压缩DP

题意: 有n个长度为m的二进制串,每个都是不同的。为了把所有字符串区分开,你可以询问,每次可以问某位上是0还是1。安排一种询问方式,使得最大的询问次数最小,输出这个次数。

解法: 设dp[s1][s2]为询问集合为s1,答案为s2,还需要询问几次能区分开。所以有:

dp[s1][s2] = 0 ,当和答案s2相同的串个数小于等于1时。

dp[s1][s2] = min(dp[s1][s2], max(dp[s1|(1<<i][ s2] ,dp[s1|(1<<i][s2|(1<<i))]+1),当s1的第i位为0时。

这题的方程式参看神牛的(点这里),ORZ。

/* **********************************************Author      : NeroCreated Time: 2013-8-27 2:25:36Problem id  : UVA 1252Problem Name: Twenty Questions*********************************************** */#include <stdio.h>#include <string.h>#include <algorithm>using namespace std;#define REP(i,a,b) for(int i=(a); i<(int)(b); i++)#define clr(a,b) memset(a,b,sizeof(a))const int INF = ~0u >> 1;int dp[(1<<11) + 10][(1<<11) + 10]; // 已选的集合,答案的集合, 还需要几次询问能区分int p[130]; // 每一根线段的答案int n,m;int dfs(int s1, int s2) {    if(dp[s1][s2] >= 0) return dp[s1][s2];    int cnt = 0;    REP(i,0,n) if((p[i]&s1) == s2) cnt ++;    if(cnt <= 1) return dp[s1][s2] = 0;    dp[s1][s2] = INF;    REP(i,0,m) {        if(s1 & (1<<i)) continue;        dp[s1][s2] = min(dp[s1][s2], max(dfs(s1|(1<<i), s2), dfs(s1|(1<<i), s2|(1<<i)))+1);    }    return dp[s1][s2];}int main() {    char s[12];    while(~scanf("%d%d", &m, &n), m || n) {        REP(i,0,n) {            p[i] = 0;            scanf("%s", s);            REP(j,0,m) {                if(s[j] == '1') {                    p[i] |= (1<<j);                }            }        }        clr(dp,-1);        printf("%d\n", dfs(0,0));    }    return 0;}


原创粉丝点击