状态压缩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 : Nero
Created Time: 2013-8-27 2:25:36
Problem id : UVA 1252
Problem 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;
}