// uva1252 Twenty Questions 状压dp
//
// 解题思路:
//
// 状压dp.
//
// dp[s,a]表示已经问的特征的集合为s,想象中物品的集合为a最少还要问
// 多少次.则状态转移为
// dp[s,a] = min(dp[s,a],max(dp[s+(k),a],dp[s+(k),a+(k)])+1);
// 我们已经知道已询问的特征集为s,则对于一个未问过的特征.我们有
// 要么这个特征是所想物品,要么不是.询问这个特征,所以最后+1.而
// 取最大值的意思,是保证一定能够猜到该物品.而对于边界条件的取值
// 就是对于一个dp[s,a]而言,能够区分一个物品与其他的物品,这时候就
// 不用在询问了.所以此时的dp[s,a] = 0.非常赞的一道题目,看了很久
// 也想了很久,想通了一些,还有一些还是有些糊涂.继续加油吧~~~
// 还有就是在统计区分物品的边界的时候,如果一开始预处理,比在递归中
// 慢了差不多四倍左右.我想,大概是有很多的状态对结果并没有贡献.
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#define cls(x,a) memset(x,a,sizeof(x))
using namespace std;
const int MAXN = 13;
const int INF = 0x1f1f1f1f;
int N,M;
int p[MAXN * 10];
int d[1<<MAXN][1<<MAXN];
bool vis[1<<MAXN][1<<MAXN];
int cnt[1<<MAXN][1<<MAXN];
inline void input(){
for (int i=0;i<N;i++){
char s[20];
scanf("%s",s);
p[i] = 0;
for (int j=0;s[j];j++)
if (s[j]=='1')
p[i] |= 1<<j;
}
cls(vis,0);
// cls(cnt,0);
// for (int i=0;i<(1<<M);i++){
// for (int j=0;j<N;j++)
// cnt[i][i & p[j]]++;
// }
}
int DP(int s,int a){
if (vis[s][a])
return d[s][a];
vis[s][a] = 1;
int cnt = 0;
for (int i=0;i<N;i++){
if ((p[i] & s) == a)
cnt++;
}
if (cnt<=1){
return d[s][a] = 0;
}
// if (cnt[s][a]<=1){
// return d[s][a] = 0;
// }
int& ans = d[s][a];
ans = INF;
for (int i=0;i<M;i++){
if (s & (1<<i))
continue;
ans = min(ans,max(DP(s|(1<<i),a),DP(s|(1<<i),a|(1<<i)))+1);
}
return ans;
}
inline void solve(){
printf("%d\n",DP(0,0));
}
int main(){
//freopen("1.txt","r",stdin);
while(scanf("%d%d",&M,&N)!=EOF){
if (N == 0 && M == 0)
break;
input();
solve();
}
return 0;
}
uva1252 Twenty Questions 状压dp
最新推荐文章于 2019-08-01 20:14:00 发布