分析

为了叙述方便,设“心里想的物体”为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;
}

最新文章

  1. AngularJS快速入门指南01:导言
  2. C#与数据库访问技术总结(六)之Command对象创建SQl语句代码示例
  3. SharedPreference 存储小量数据,一般首次启动显示引导界面就用这个。
  4. 设置图片Alpha
  5. 数据结构之单链表,c#实现
  6. 我的java学习笔记(一)
  7. Installing MySQL on Microsoft Windows Using a noinstall Zip Archive
  8. 用letsencrypt搭建免费的https网站
  9. Akka.net 性能测试兼使用小技巧
  10. ssh 连接失败 sz rz 安装
  11. css的小知识3
  12. javascript中的LHS和RHS
  13. tmux 快捷操作
  14. UVa 10697 - Firemen barracks
  15. Google 地图切片URL地址解析
  16. Python_07-常用函数
  17. EMQ ---websocket
  18. 【BZOJ3211】花神游历各国 并查集+树状数组
  19. Ubuntu命令行下安装、卸载、管理软件包的方法
  20. BZOJ1030:[JSOI2007]文本生成器——题解

热门文章

  1. oracle 临时表的使用
  2. python3 操作windows的粘贴板(读取和传值)
  3. codeforces 966c//Big Secret// Codeforces Round #477 (Div. 1)
  4. homestead 添加新站点
  5. C++ vector 实现二维数组
  6. OC 复合
  7. [LeetCode] 268. Missing Number ☆(丢失的数字)
  8. Sql Server约束的学习一(主键约束、外键约束、唯一约束)
  9. 【转载】maven入门1
  10. 设置xml中控件的圆润边框效果