【UVa】【DP】1252 Twenty Questions

本文介绍了一种解决UVa1252 Twenty Questions问题的方法,通过状态压缩动态规划(DP)实现最少询问次数以确定目标物体。讨论了如何利用二进制整数表示特征集合,并详细解释了状态转移方程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

UVa 1252 Twenty Questions

题目

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

题目大意

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

思路

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

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

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

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

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

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

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

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

其中, 0iM 0 ≤ i ≤ M

接下来讨论边界条件。

若只有一个物体满足:具备集合 a a 中的所有特征,但不具备集合sa中的所有特征的时候,此时的 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值