Twenty Questions UVA - 1252 (状压DP)

题意如下:

给出M个特征和N个物体。每个物体有M个特征中的某一些。现在给出N个物体中的一个物体,可以询问是否具有某项特征,问至少需要多少次询问,才能确认这个物体。

题目链接:https://vjudge.net/problem/UVA-1252

题目分析:因为特征数最多只有11个,很容易的想到状压DP。而且这个问题是一个取与不取得问题,那状态肯定有两种状压方式。对于当前阶段,就有取和不取两个衍生点。我们令DP[S][A]表示在考虑了S集合的特征之后,具有了A集合特征的01字符串(物体)个数。详见代码。

代码如下:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<string>

using namespace std;
const int maxx = (1<<11)+500;
int n,m;
int dp[maxx][maxx];
int cnt[maxx][maxx];//cnt用来预处理,cnt[s][a]表示已考虑特征集合为S,当前具有的特征集合为A时候的有几个满足的01字符串。
char str[20];

void init(){
memset(dp,-1,sizeof(dp));
memset(cnt,0,sizeof(cnt));
}

void insertStr(char s[]){//题目给出的事01串,我们需要把他看成二进制数字然后转化为十进制。
int len = strlen(s);
int aa = 0;
for(int i=len-1;i>=0;i--){
    if(s[i]=='1')aa|=(1<<(len-i-1));
}

for(int i=0;i<(1<<n);i++){//对于每一个字符串,找到所有S和他的共同特征集合并计数。
   cnt[i][i&aa]++;
}
}


int dfs(int s,int a)
{
    if(cnt[s][a]==1)return 0;//如果当前考虑集合为S,只有一个01字符串满足A,那么就唯一识别了一个字符串,还需要0次询问
    if(cnt[s][a]==2)return 1;//如果还有两个字符串需要区分,那么只需要一次询问就能区分。
    int &ans = dp[s][a];
    if(ans!=-1)return ans;
    ans = n;
    for(int k=0;k<n;k++){
      int aa = (1<<k);
      int res = 0;
      if((s&aa)==0){
        int s2 = s|aa;//当前总的集合增加
        int a2 = a|aa;//选取该询问
        if(cnt[s2][a]>=1&&cnt[s2][a2]>=1){
            res = max(dfs(s2,a),dfs(s2,a2))+1;//为什么要取最大值呢?
            //因为题目意思是要我们找到最小的询问次数,使得无论我们找哪一个字符串,我们都能找到(which every object in the set is identifiable)。
            //所以我们必须考虑最难找的情况。
              ans = min(ans,res);//但是我们可以通过调整询问问题的次序的不同来找到最小的res(最大次数)。
        }
      }
    }
return ans;
}

int main(){
while(scanf("%d%d",&n,&m)!=EOF)
{
    if(n==0&&m==0)break;
    init();
 for(int i=0;i<m;i++){
    scanf("%s",str);
    insertStr(str);
 }
int res = dfs(0,0);
printf("%d\n",res);
}
return 0;
}
PS:状态不是很好想,不过以后记住这种操作就可以了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值