UVa 1252 Twenty Questions
题目
◇题目传送门◆(由于UVa较慢,这里提供一份vjudge的链接)
◇题目传送门(vjudge)◆
题目大意
有 N N 个物体,个特征。每个物体用一个 M M 位的01串表示,其中1表示该物体存在这个特征,0表示不存在。现在我在心中想一个物体(在这个物体之中),由你来猜。
你每次可以询问一个特征,然后我会告诉你该物体是否具备这个特征。当你确定这个物品后,就把答案告诉我(告诉答案不算询问)。求你最少需要几次询问才能够保证猜到。
思路
为了表述方便,以下记 A A 为我心中所想的物品。
由于非常小,所以我们可以使用一个二进制整数来表示特征集合。
自然我们就将这道题转化为了状压DP。
我们使用贪心的想法可以知道,同一特征不需要问两遍。所以我们可以使用一个集合 s s 表示已经询问的特征集合;在这个集合中,有些特征是所具备的。所以我们再使用一个集合 a a 表示已经确定物品具备的特征集合。不难发现 a a 是的一个子集。
定义状态 f(s,a) f ( s , a ) 为已经询问了的特征集合为 s s ,其中已经确定的特征集合为时,还需要询问的最少次数。若下一次对特征 i i 进行提问,则询问次数就是:
考虑所有的 i i ,则得到状态转移方程:
其中, 0≤i≤M 0 ≤ i ≤ M 。
接下来讨论边界条件。
若只有一个物体满足:具备集合 a a 中的所有特征,但不具备集合中的所有特征的时候,此时的 d(s,a)=0 d ( s , a ) = 0 ,因为无需进一步询问就可以得到答案。
正解代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int Maxn=128;
const int Maxm=11;
const int INF=0x3f3f3f3f;
int N,M,st[Maxn+5];
int f[1<<Maxm+1][1<<Maxm+1];
int Same(int s,int a) {
int num=0;
for(int i=1;i<=N;i++)
if((st[i]&s)==a)
num++;
return num;
}//计算满足具备集合a中的所有特征,但不具备集合s-a中的所有特征的物品数量
int DFS(int s,int a) {
int &res=f[s][a];
if(res!=-1)
return res;
if(Same(s,a)<=1)
return f[s][a]=0;//边界条件
int ret=INF;
for(int i=0;i<M;i++) {
if(s&(1<<i))continue;
int t1=DFS(s|(1<<i),a);
int t2=DFS(s|(1<<i),a|(1<<i));
ret=min(ret,max(t1,t2));
}
return res=ret+1;
}
int main() {
#ifdef LOACL
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
while(scanf("%d %d",&M,&N)!=EOF&&N&&M) {
for(int i=1;i<=N;i++) {
char tmp[20];
scanf("%s",tmp);//注意读入使用字符串
for(int j=0;j<M;j++)
if(tmp[j]=='1')
st[i]|=(1<<j);
}
memset(f,-1,sizeof f);
printf("%d\n",DFS(0,0));
memset(st,0,sizeof st);//注意清空特征数组
}
return 0;
}