分析

为了叙述方便,设“心里想的物体”为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)可以忽略不计。

代码实现

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. template<class T> inline T read(T&x)
  4. {
  5. T data=0;
  6. int w=1;
  7. char ch=getchar();
  8. while(ch!='-'&&!isdigit(ch))
  9. ch=getchar();
  10. if(ch=='-')
  11. w=-1,ch=getchar();
  12. while(isdigit(ch))
  13. data=10*data+ch-'0',ch=getchar();
  14. return x=data*w;
  15. }
  16. const int maxn=150;
  17. int m,n;
  18. int feature[maxn],cnt[(1<<11)+10][(1<<11)+10];
  19. int d[(1<<11)+10][(1<<11)+10];
  20. int dp(int s,int a){
  21. if(d[s][a]!=-1)
  22. return d[s][a];
  23. if(cnt[s][a]<=1)
  24. return d[s][a]=0;
  25. if(cnt[s][a]==2)
  26. return d[s][a]==1;
  27. int ans=20;
  28. for(int i=0;i<m;++i)
  29. if(!(s&(1<<i)))
  30. ans=min(ans, max(dp(s|(1<<i),a),dp(s|(1<<i),a|(1<<i)))+1 );
  31. return d[s][a]=ans;
  32. }
  33. int main()
  34. {
  35. while(read(m)&&read(n))
  36. {
  37. memset(feature,0,sizeof(feature));
  38. char s[20];
  39. for(int i=1;i<=n;++i)
  40. {
  41. scanf("%s",s);
  42. for(int j=0;j<m;++j)
  43. feature[i]|=((s[j]-'0')<<j);
  44. }
  45. /* clog<<"input check"<<endl;
  46. for(int i=1;i<=n;++i)
  47. {
  48. for(int j=0;j<m;++j)
  49. clog<<((feature[i]&(1<<j))?1:0);
  50. clog<<endl;
  51. }
  52. clog<<"input check completed"<<endl;*/
  53. memset(cnt,0,sizeof(cnt));
  54. for(int i=0;i<(1<<m);++i)
  55. for(int j=1;j<=n;++j)
  56. ++cnt[i][i&feature[j]];
  57. memset(d,-1,sizeof(d));
  58. printf("%d\n",!dp(0,0)?0:dp(0,0)+1);
  59. }
  60. return 0;
  61. }

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

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

  1. # include<iostream>
  2. # include<cstdio>
  3. # include<string>
  4. # include<cstring>
  5. # include<algorithm>
  6. using namespace std;
  7. const int INF=0x3f3f3f3f;
  8. char p[13];
  9. int dp[1<<11][1<<11],sta[130],m,n;
  10. int getVal()
  11. {
  12. int res=0;
  13. for(int i=0;i<m;++i)
  14. if(p[i]=='1')
  15. res|=(1<<i);
  16. return res;
  17. }
  18. int DP(int s,int a)
  19. {
  20. if(dp[s][a]!=INF)
  21. return dp[s][a];
  22. int num=0;
  23. for(int i=0;i<n;++i)///在这里,也可以预处理出来以提高效率;
  24. if((sta[i]&s)==a)///"=="的优先级比"&"的高!!!
  25. ++num;
  26. if(num<=1)
  27. return dp[s][a]=0;
  28. int &ans=dp[s][a];
  29. for(int i=0;i<m;++i){
  30. if(s&(1<<i))
  31. continue;
  32. ans=min(ans,max(DP(s|(1<<i),a),DP(s|(1<<i),a|(1<<i)))+1);
  33. }
  34. return ans;
  35. }
  36. int main()
  37. {
  38. while(scanf("%d%d",&m,&n)&&n+m)
  39. {
  40. for(int i=0;i<n;++i){
  41. scanf("%s",p);
  42. sta[i]=getVal();
  43. }
  44. memset(dp,INF,sizeof(dp));
  45. printf("%d\n",DP(0,0));
  46. }
  47. return 0;
  48. }

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. HTML表单组件

    HTML表单组件 一.说明 form标签里面的东西 二.效果图 三.代码 <!DOCTYPE html> <html> <head> <title>Fo ...

  2. 『OpenCV3』简单图片处理

    cv2和numpy深度契合,其图片读入后就是numpy.array,只不过dtype比较不常用而已,支持全部数组方法 数组既图片 import numpy as np import cv2 img = ...

  3. dp练习(11)——石子并归

    1048 石子归并  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 黄金 Gold 题解       题目描述 Description 有n堆石子排成一列,每堆石子有一个重量w ...

  4. Queue 实现生产者消费者模型

    Python中,队列是线程间最常用的交换数据的形式. Python Queue模块有三种队列及构造函数: 1.Python Queue模块的FIFO队列先进先出. class Queue.Queue( ...

  5. ASP.NET的内置对象 —— Request 对象

    Request 对象最大的用途在于提交表单信息. (可获取页面间传递的值.客户端的 IP 地址等) 3.2.2 获取页面间传送的值 获取页面传送参数值是 Request 对象最广泛的应用之一. ASP ...

  6. OAF 通过个性化 在标准事件上添加验证

    在实际的开发过程中,我们经常会遇到以下情况: 在执行标准的功能之前要对个性化的内容进行校验. 比如:在某个标准页面通过个性化添加了一个勾选框,在点击下一步的时候必须去验证此勾选框是否勾选. 具体实现如 ...

  7. 教你一步一步用 Node.js 制作慕课网视频爬虫

    转自:http://www.jianshu.com/p/d7631fc695af 开始 这个教程十分适合初学 Node.js 的初学者看(因为我也是一只初学的菜鸟~) 在这里,我就默认大家都已经在自己 ...

  8. consumer filter

    ProtocolFilterWrapper中buildInvokerChain方法把Filter链在一起,调用执行的时候,逐个执行filter,最后执行filter中的invoker. //Proto ...

  9. 如何用SPY++工具查看窗体的句柄

    我安装的是vs2012,先找到SPY++工具打开 打开方式: 方式1:通过路径(C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Microso ...

  10. linux processes

    So that Linux can manage the processes in the system, each process is represented by a task_struct   ...