M M 个特征。每个物体用一个MM位的01串表示,其中1表示该物体存在这个特征,0表示不存在。现在我在心中想一个物..." />
当前位置: 代码迷 >> 综合 >> 【UVa】【DP】1252 Twenty Questions
  详细解决方案

【UVa】【DP】1252 Twenty Questions

热度:39   发布时间:2023-11-21 07:05:22.0

UVa 1252 Twenty Questions

题目

◇题目传送门◆(由于UVa较慢,这里提供一份vjudge的链接)
◇题目传送门(vjudge)◆

题目大意

NN个物体, M 个特征。每个物体用一个MM位的01串表示,其中1表示该物体存在这个特征,0表示不存在。现在我在心中想一个物体(在这 N 个物体之中),由你来猜。
你每次可以询问一个特征,然后我会告诉你该物体是否具备这个特征。当你确定这个物品后,就把答案告诉我(告诉答案不算询问)。求你最少需要几次询问才能够保证猜到。

思路

为了表述方便,以下记AA为我心中所想的物品。

由于 M 非常小,所以我们可以使用一个二进制整数来表示特征集合。

自然我们就将这道题转化为了状压DP。

我们使用贪心的想法可以知道,同一特征不需要问两遍。所以我们可以使用一个集合ss表示已经询问的特征集合;在这个集合中,有些特征是 A 所具备的。所以我们再使用一个集合aa表示已经确定物品 A 具备的特征集合。不难发现aa s 的一个子集。

定义状态f(s,a)f(s,a)为已经询问了的特征集合为ss,其中已经确定的特征集合为 a 时,还需要询问的最少次数。若下一次对特征ii进行提问,则询问次数就是:

max { f ( s + { i } , a + { i } ) , f ( s + { i } , a ) } + 1

考虑所有的ii,则得到状态转移方程:

f ( s , a ) = min { f ( s , a ) , max { f ( s + { i } , a + { i } ) , f ( s + { i } , a ) } + 1 }

其中,0iM0≤i≤M

接下来讨论边界条件。

若只有一个物体满足:具备集合aa中的所有特征,但不具备集合 s ? a 中的所有特征的时候,此时的d(s,a)=0d(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 LOACLfreopen("in.txt","r",stdin);freopen("out.txt","w",stdout);#endifwhile(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;
}