【状压dp】UVA1252二十个问题

题目大意:有n件物品,每件物品有m个特征,可以对特征进行询问,询问的结果是得知某个物体是否含有该特征,要把所有的物品区分出来(n个物品的特征都互不相同)至少需要多少次询问?

考虑状态的表示,可以用一个集合s1表示已经询问过的特征集合,s2表示已经确定物体有的特征集合。在每个状态下,考虑询问哪个特征。首先明确一个问题,在一个状态下,问哪个特征是由你决定,但得到什么回答就不一定了。比如询问一个所有物体中只有一个物体有的特征,如果回答yes那么一击即中。但更坏的情况是回答no,那么还得继续向下找。所以在这个状态下,就要想象对方故意难为你,回答让你询问更多次数的答案。但是选择询问什么是你自己决定的,这就是取min和max的含义。当cnt[s1][s2]=1时是边界,这时无需再询问,因为只有一个物体合法了。

细节比较难处理,cnt[s1][s2]表示询问集合s1得到含有特征集合s2的物体的个数,这个预处理开始没想出来。感觉AC是因为跟着题解的思路走的,很多细节还要再考虑。如果s1,s2状态下没有这样一个物体,也就是状态不合法。这样的话cnt[s1][s2]=0,就可以通过cnt的数值判断出状态合不合法,如果不合法就返回0,取max的时候就会忽略这个状态。然后顺着这个又可以想到,万一有两个转移状态都不合法怎么办?首先,如果走到转移这一步,cnt[s1][s2]肯定>=2,cnt[s1][s2]>=1说明此状态合法。那么询问的这个特征,目标物体要么有要么没有,所以回答有和没有的状态中肯定有一个是合法的

#include<iostream>
#include<cstdio>
#include<cstring>
#define INF (20010)
using namespace std;
int m,n,A[2020],cnt[(1<<11)+5][(1<<11)+5],dp[(1<<11)+5][(1<<11)+5];
char s[2020];
bool vis[(1<<11)+5][(1<<11)+5];
int Dfs(int s1,int s2){
	int &ans=dp[s1][s2];
	if (vis[s1][s2]) return ans;
	vis[s1][s2]=1;
	if (cnt[s1][s2]<=1) return ans=0;
	ans=INF;
	int i;
	for (i=1;i<=m;i++)
		if (!((1<<(i-1))&s1)) 
			ans=min(ans,max(Dfs(s1|(1<<(i-1)),s2|(1<<(i-1))),Dfs(s1|(1<<(i-1)),s2))+1);
	return ans;
}
void Work(){
	int i,j,len;
	memset(A,0,sizeof(A));
	memset(cnt,0,sizeof(cnt));
	memset(vis,0,sizeof(vis));
	for (i=1;i<=n;i++){
		scanf("%s",s);
		len=strlen(s);
		for (j=0;j<len;j++) if (s[j]-'0') A[i]|=(1<<j); 
	}
	for (i=0;i<=(1<<m)-1;i++)
		for (j=1;j<=n;j++)
			cnt[i][i&A[j]]++;
	printf("%d\n",Dfs(0,0));
}
int main(){
	while (cin>>m>>n){
		if (!m&&!n) break;
		Work();
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值