uva1252 Twenty Questions 状压dp

14 篇文章 0 订阅
7 篇文章 0 订阅
//	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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值