分析

为了叙述方便,设“心里想的物体”为W。首先在读入时把每个物体转化为一个二进制整数。不难发现,同一个特征不需要问两遍,所以可以用一个集合s表示已经询问的特征集。

在这个集合s中,有些特征是W所具备的,剩下的特征是W不具备的。用集合a来表示“已确认物体W具备的特征集”,则a一定是s的子集。

设d(s,a)表示已经问了特征集s,其中已确认W所具备的特征集为a时,还需要询问的最小次数。如果下一次提问的对象是特征k(这就是“决策”),则询问次数为:

max{d(s+{k},a+{k}),d(s+{k}, a)}+1

考虑所有的k,取最小值即可。边界条件为:如果只有一个物体满足“具备集合a中的所有特征,但不具备集合s-a中的所有特征”这一条件,则d(s,a)=0,因为无须进一步询问,已经可以得到答案。

因为a为s的子集,所以状态总数为3m,时间复杂度为O(m3^m)。对于每个s和a,可以先把满足该条件的物体个数统计出来,保存在cnt[s][a],避免状态转移的时候重复计算。统计cnt[s][a]的方法是枚举s和物体,时间复杂度为O(n2^m),所以总时间复杂度为O(n2^m +m3^m)。对于本题的规模来说O(n*2^m)可以忽略不计。

代码实现

#include<bits/stdc++.h>
using namespace std;
template<class T> inline T read(T&x)
{
    T data=0;
    int w=1;
    char ch=getchar();
    while(ch!='-'&&!isdigit(ch))
        ch=getchar();
    if(ch=='-')
        w=-1,ch=getchar();
    while(isdigit(ch))
        data=10*data+ch-'0',ch=getchar();
    return x=data*w;
}
const int maxn=150;
int m,n;
int feature[maxn],cnt[(1<<11)+10][(1<<11)+10];
int d[(1<<11)+10][(1<<11)+10];

int dp(int s,int a){
    if(d[s][a]!=-1)
        return d[s][a];
    if(cnt[s][a]<=1)
        return d[s][a]=0;
    if(cnt[s][a]==2)
        return d[s][a]==1;
    int ans=20;
    for(int i=0;i<m;++i)
        if(!(s&(1<<i)))
            ans=min(ans, max(dp(s|(1<<i),a),dp(s|(1<<i),a|(1<<i)))+1 );
    return d[s][a]=ans;
}

int main()
{
    while(read(m)&&read(n))
    {
        memset(feature,0,sizeof(feature));
        char s[20];
        for(int i=1;i<=n;++i)
        {
            scanf("%s",s);
            for(int j=0;j<m;++j)
                feature[i]|=((s[j]-'0')<<j);
        }
/*      clog<<"input check"<<endl;
        for(int i=1;i<=n;++i)
        {
            for(int j=0;j<m;++j)
                clog<<((feature[i]&(1<<j))?1:0);
            clog<<endl;
        }
        clog<<"input check completed"<<endl;*/
        memset(cnt,0,sizeof(cnt));
        for(int i=0;i<(1<<m);++i)
            for(int j=1;j<=n;++j)
                ++cnt[i][i&feature[j]];
        memset(d,-1,sizeof(d));
        printf("%d\n",!dp(0,0)?0:dp(0,0)+1);
    }
    return 0;
}

另外,输入物体和预处理cnt[s][a]时刘汝佳标程的做法太繁琐,大家可以参考我的做法。

网上有大量AC代码没有预处理,大概比我慢了70ms,而我跑出来是80ms,所以差别不大。有的人可能喜欢不加预处理,这里我也提供一份朴素的代码(不是我打的,有问题别找我)

# include<iostream>
# include<cstdio>
# include<string>
# include<cstring>
# include<algorithm>
using namespace std;

const int INF=0x3f3f3f3f;

char p[13];
int dp[1<<11][1<<11],sta[130],m,n;

int getVal()
{
    int res=0;
    for(int i=0;i<m;++i)
        if(p[i]=='1')
            res|=(1<<i);
    return res;
}

int DP(int s,int a)
{
    if(dp[s][a]!=INF)
        return dp[s][a];

    int num=0;
    for(int i=0;i<n;++i)///在这里,也可以预处理出来以提高效率;
        if((sta[i]&s)==a)///"=="的优先级比"&"的高!!!
            ++num;
    if(num<=1)
        return dp[s][a]=0;

    int &ans=dp[s][a];
    for(int i=0;i<m;++i){
        if(s&(1<<i))
            continue;
        ans=min(ans,max(DP(s|(1<<i),a),DP(s|(1<<i),a|(1<<i)))+1);
    }
    return ans;
}

int main()
{
    while(scanf("%d%d",&m,&n)&&n+m)
    {
        for(int i=0;i<n;++i){
            scanf("%s",p);
            sta[i]=getVal();
        }
        memset(dp,INF,sizeof(dp));
        printf("%d\n",DP(0,0));
    }
    return 0;
}

UVA1252 【Twenty Questions】的更多相关文章

  1. 【Uva 1252】Twenty Questions

    [Link]: [Description] 给你n个物体,每个物体都有m种属性; (每个物体的属性都能和别的物体的属性区别) 现在,你已知这n个物体; 然后让一个人心里想一个物体 你可以问这个人,这个 ...

  2. Uva1252 Twenty Questions

    Twenty Questions https://odzkskevi.qnssl.com/15b7eb4cd1f75f63cee3945b0b845e4f?v=1508411736 [题解] dp[S ...

  3. 【android studio】android studio使用过程中,搜集的一些问题

    1.[知乎]在Android Studio中如何将依赖的jar包放在SDK的android.jar前? 在编译原生Contacts应用时需用到非公开的API,需要引入framework等jar包,但在 ...

  4. 【netcore基础】.Net core通过 Lucene.Net 和 jieba.NET 处理分词搜索功能

    业务要求是对商品标题可以进行模糊搜索 例如用户输入了[我想查询下雅思托福考试],这里我们需要先将这句话分词成[查询][雅思][托福][考试],然后搜索包含相关词汇的商品. 思路如下 首先我们需要把数据 ...

  5. 【Java基础】反射和注解

    前言 在Java中,反射机制和注解机制一直是一个很重要的概念,那么他们其中的原理是怎么样呢,我们不仅仅需要会使用,更要知其然而之所以然. 目录 反射机制 反射如何使用 注解定义 注解机制原理 注解如何 ...

  6. 【Spring实战】----开篇(包含系列目录链接)

    [Spring实战]----开篇(包含系列目录链接) 置顶2016年11月10日 11:12:56 阅读数:3617 终于还是要对Spring进行解剖,接下来Spring实战篇系列会以应用了Sprin ...

  7. 反爬虫:利用ASP.NET MVC的Filter和缓存(入坑出坑) C#中缓存的使用 C#操作redis WPF 控件库——可拖动选项卡的TabControl 【Bootstrap系列】详解Bootstrap-table AutoFac event 和delegate的分别 常见的异步方式async 和 await C# Task用法 c#源码的执行过程

    反爬虫:利用ASP.NET MVC的Filter和缓存(入坑出坑)   背景介绍: 为了平衡社区成员的贡献和索取,一起帮引入了帮帮币.当用户积分(帮帮点)达到一定数额之后,就会“掉落”一定数量的“帮帮 ...

  8. 【python系列】python画报表(Chartkick、Flask)(附中文乱码解决方式)

    chartkick 能够画 javascript 报表, 并且比較美观.可是网上搜了下.非常难找到 python 版本号的,于是查了些资料,摸索了下. 对 Flask 也不非常熟悉,这里就仅仅抛砖引玉 ...

  9. 【33.10%】【codeforces 604C】Alternative Thinking

    time limit per test2 seconds memory limit per test256 megabytes inputstandard input outputstandard o ...

随机推荐

  1. Style、ControlTemplate 和 DataTemplate 触发器

    本文摘要:    1:属性触发器:    2:数据触发器:    3:事件触发器: Style.ControlTemplate 和 DataTemplate 都有触发器集合.    属性触发器只检查W ...

  2. English trip -- Review Unit5 Around town 在城市

    restaurant 餐厅 supermarket 超市 shoping mall 购物中心 drugstore 药店 hospital 医院 laundromat  洗衣店 moive threat ...

  3. Oracle 11g dataguard check real time apply

    2017年8月24日 16:38 环境:oracle 11.2.0.1 OEL-5.8 注:以下操作都在备库执行 总结方法: 1.FPYJ(125_7)@fpyj123> select open ...

  4. redhat linux 6.2 安装配置GUI

    redhat6.2默认不安装GUI,启动时默认进入text模式,下面介绍下安装.配置GUI的步骤: 1.登录root 2.配置及测试yum   vi /etc/yum.repos.d/rhel-sou ...

  5. [LeetCode] 41. First Missing Positive ☆☆☆☆☆(第一个丢失的正数)

    Given an unsorted integer array, find the smallest missing positive integer. Example 1: Input: [1,2, ...

  6. Java中String和byte[]间的 转换浅析

    Java语言中字符串类型和字节数组类型相互之间的转换经常发生,网上的分析及代码也比较多,本文将分析总结常规的byte[]和String间的转换以及十六进制String和byte[]间相互转换的原理及实 ...

  7. css 页面定位position

    position的四个属性值 relative absolute fixed static 参看实例 <div id="parent">  <div id='su ...

  8. 快速切题 sgu117. Counting 分解质因数

    117. Counting time limit per test: 0.25 sec. memory limit per test: 4096 KB Find amount of numbers f ...

  9. [转载]彻底卸载oracleXE数据库服务器

    URL:http://www.2cto.com/database/201306/216182.html

  10. Django(四)模板文件中的循环

    编辑views.py from django.shortcuts import render from django.shortcuts import HttpResponse #此行增加 # Cre ...