• 侦探推理


    来源:NOIP2003提高组 https://ac.nowcoder.com/acm/contest/251/B

    明明同学最近迷上了侦探漫画《柯南》并沉醉于推理游戏之中,于是他召集了一群同学玩推理游戏。游戏的内容是这样的,明明的同学们先商量好由其中的一个人充当罪犯(在明明不知情的情况下),明明的任务就是找出这个罪犯。接着,明明逐个询问每一个同学,被询问者可能会说:

    证词中出现的其他话,都不列入逻辑推理的内容。

    明明所知道的是,他的同学中有N个人始终说假话,其余的人始终说真。

    现在,明明需要你帮助他从他同学的话中推断出谁是真正的凶手,请记住,凶手只有一个!

    输入描述:
    输入由若干行组成,第一行有二个整数,M(1≤M≤20)、N(1≤N≤M)和P(1≤P≤100);M是参加游戏的明明的同学数,N是其中始终说谎的人数,P是证言的总数。接下来M行,每行是明明的一个同学的名字(英文字母组成,没有主格,全部大写)。
    往后有P行,每行开始是某个同学的名宇,紧跟着一个冒号和一个空格,后面是一句证词,符合前表中所列格式。证词每行不会超过250个字符。
    输入中不会出现连续的两个空格,而且每行开头和结尾也没有空格。



    输出描述:
    如果你的程序能确定谁是罪犯,则输出他的名字;如果程序判断出不止一个人可能是罪犯,则输出 Cannot Determine;如果程序判断出没有人可能成为罪犯,则输出 Impossible。
    示例1

    输入

    3 1 5
    MIKE
    CHARLES
    KATE
    MIKE: I am guilty.
    MIKE: Today is Sunday.
    CHARLES: MIKE is guilty.
    KATE: I am guilty.
    KATE: How are you??

    输出

    MIKE

    算法知识点: 枚举,模拟,字符串处理

    复杂度:O(7M^2P)

    解题思路:

    对于比较繁琐的模拟题,写代码的时候建议尽可能模块化。
    依次枚举每个同学是否可能是凶手。最终结果有三种:
    可能的凶手只有一个,输出凶手名字;
    可能的凶手多于一个,输出"Cannot Determine";
    任何一个同学都不可能是凶手,输出"Impossible";
    在枚举每个同学是凶手的过程中,依次枚举今天是星期几,然后判断说谎的同学是否有可能有 n 个。

    这里有两点需要注意:
    每个同学需要始终如一,如果出现某个同学既说真话又说假话,那么当前枚举的情况不合法;
    每个句子有三种情况:真话、假话、废话。最终只要 假话数量 ≤n≤ 假话数量 + 废话数量,那么说谎的同学就有可能有 n 个;

    时间复杂度分析:

    我们会依次枚举凶手、星期几以及句子,对于每个句子在判断是谁说的时会再循环一遍所有同学,所以总时间复杂度是 O(7M^2P),其中 M 是总人数,P 是总句子数。

      1 #include <cstring>
      2 #include <iostream>
      3 #include <algorithm>
      4 using namespace std;
      5 const int N = 110,
      6     M = 30;
      7  
      8 int m, n, P;
      9 string sentence[N], name[M];
     10 string weekday[7] = {
     11     "Monday",
     12     "Tuesday",
     13     "Wednesday",
     14     "Thursday",
     15     "Friday",
     16     "Saturday",
     17     "Sunday"
     18 };
     19 int ch[M];
     20  
     21 pair <int, string> get(string str) // 对于每句话,剥离出说话者和说的话
     22 {
     23     int t = str.find(':');
     24     string person = str.substr(0, t);
     25     string sent = str.substr(t + 2);
     26  
     27     for (int i = 0; i < m; i++)
     28         if (name[i] == person)
     29             return {
     30                 i,
     31                 sent
     32             };
     33 }
     34  
     35 bool endwith(string s1, string s2) // 判断s1是否以s2结尾
     36 {
     37     int i = s1.find(s2);
     38     if (s1.size() - i == s2.size()) return true;
     39 }
     40  
     41 int judge(int bad_man, int p, int day, string sent) // 判断每句话的真假,真话返回0,假话返回1,无意义的话返回2
     42 {
     43     if (sent == "I am guilty.")
     44     {
     45         if (p == bad_man) return 0;
     46         return 1;
     47     }
     48     if (sent == "I am not guilty.")
     49     {
     50         if (p != bad_man) return 0;
     51         return 1;
     52     }
     53     if (endwith(sent, " is guilty."))
     54     {
     55         int t = sent.find(" is guilty."), now = -1;
     56         for (int i = 0; i < m; i++)
     57             if (sent.substr(0, t) == name[i])
     58             {
     59                 now = i;
     60                 break;
     61             }
     62         if (now == -1) return 2;
     63  
     64         if (now == bad_man) return 0;
     65         return 1;
     66     }
     67     if (endwith(sent, " is not guilty."))
     68     {
     69         int t = sent.find(" is not guilty."), now = -1;
     70         for (int i = 0; i < m; i++)
     71             if (sent.substr(0, t) == name[i])
     72             {
     73                 now = i;
     74                 break;
     75             }
     76         if (now == -1) return 2;
     77  
     78         if (now != bad_man) return 0;
     79         return 1;
     80     }
     81     if (sent.substr(0, 9) == "Today is ")
     82     {
     83         int now = -1;
     84         for (int i = 0; i < 7; i++)
     85             if (sent.substr(9) == weekday[i] + ".")
     86             {
     87                 now = i;
     88                 break;
     89             }
     90  
     91         if (now == -1) return 2;
     92  
     93         if (now == day) return 0;
     94         return 1;
     95     }
     96  
     97     return 2;
     98 }
     99  
    100 bool check(int bad_man, int day) // 判断凶手是bad_man,今天是day的情况下,时候可能有n个同学说谎
    101 {
    102     memset(ch, -1, sizeof ch);
    103  
    104     for (int i = 0; i < P; i++)
    105     {
    106         auto ret = get(sentence[i]);
    107         int p = ret.first;
    108         string sent = ret.second;
    109  
    110         int t = judge(bad_man, p, day, sent);
    111  
    112         if (!t) // 真话
    113         {
    114             if (ch[p] == -1) ch[p] = 0;
    115             else if (ch[p] == 1) return false;
    116         }
    117         else if (t == 1) // 假话
    118         {
    119             if (ch[p] == -1) ch[p] = 1;
    120             else if (ch[p] == 0) return false;
    121         }
    122     }
    123  
    124     int c1 = 0, c2 = 0;
    125     for (int i = 0; i < m; i++)
    126         if (ch[i] == 1)
    127             c1++;
    128         else if (ch[i] == -1)
    129         c2++;
    130  
    131     return c1 <= n && c1 + c2 >= n;
    132 }
    133  
    134 int main()
    135 {
    136     cin >> m >> n >> P;
    137     for (int i = 0; i < m; i++) cin >> name[i];
    138  
    139     getline(cin, sentence[0]);
    140     for (int i = 0; i < P; i++) getline(cin, sentence[i]);
    141  
    142     int cnt = 0, p;
    143     for (int i = 0; i < m; i++)
    144     {
    145         for (int day = 0; day < 7; day++)
    146             if (check(i, day))
    147             {
    148                 p = i;
    149                 cnt++;
    150                 break;
    151             }
    152     }
    153  
    154     if (!cnt) puts("Impossible");
    155     else if (cnt == 1) cout << name[p] << endl;
    156     else puts("Cannot Determine");
    157  
    158     return 0;
    159 }
     1 #include<iostream> 
     2 #include<cstring>
     3 #include<string>
     4 using namespace std;
     5 int n,m,p,fake[21],err,w[200],nx;
     6 string name[100],say[200];
     7 string day[10]={"233","Today is Sunday.","Today is Monday.","Today is Tuesday.","Today is Wednesday.","Today is Thursday.","Today is Friday.","Today is Saturday.",};
     8 void set(int who,int yx){
     9     //err是错误标记,如果一个人既说真话又说假话显然是不吼滴 
    10     if(fake[who]&&fake[who]!=yx)err=1;
    11     else fake[who]=yx;
    12 }
    13 int main(){
    14     cin>>m>>n>>p;
    15     for(int i=1;i<=m;i++)
    16         cin>>name[i];
    17     for(int i=1;i<=p;i++){
    18         string nm;
    19         cin>>nm;//输入这句话是谁说的 
    20         nm.erase(nm.end()-1);//把冒号搞掉 
    21  
    22         for(int j=1;j<=m;j++)
    23         if(name[j]==nm)w[i]=j; //判断这句话是谁说的
    24  
    25         getline(cin,say[i]);
    26         say[i].erase(say[i].begin());//把空格搞掉 
    27     //  say[i].erase(say[i].end()-1);
    28     //把换行符去掉,上一行注释如果不去掉自己电脑能过,去掉评测能过。。。 
    29     }
    30     for(int td=1;td<=7;td++)//枚举今天是星期几 
    31     for(int px=1;px<=m;px++){//枚举罪犯是谁 
    32  
    33         //清除标记 
    34         err=0;
    35         memset(fake,0,sizeof(fake)); 
    36  
    37         for(int i=1;i<=p;i++){//依次判断每句话 
    38             int who=w[i];
    39  
    40             if(say[i]=="I am guilty.")set(who,px==who?1:-1);
    41             //如果一个人是罪犯,而且说自己是罪犯,那他说的就是真话,否则就是假话 
    42  
    43             if(say[i]=="I am not guilty.")set(who,px!=who?1:-1);
    44             //如果一个人不是罪犯,而且说自己不是罪犯,那他说的就是真话,否则就是假话 
    45  
    46             for(int j=1;j<=7;j++)
    47             if(say[i]==day[j])set(who,j==td?1:-1);
    48             //如果一个人说今天是星期几,说对了就是真话,说错了就是假话 
    49             for(int j=1;j<=m;j++){
    50                 if(say[i]==name[j]+" is guilty.")set(who,j==px?1:-1);
    51                 if(say[i]==name[j]+" is not guilty.")set(who,j!=px?1:-1);
    52                 //显然说对了就是真话,否则就是假话 
    53             }
    54         }
    55         int cnt=0,ppp=0;//cnt:说假话的人数,ppp:不确定的人数 
    56         for(int i=1;i<=m;i++){
    57             if(fake[i]==-1)cnt++;
    58             if(fake[i]==0)ppp++;
    59         }
    60         if(!err&&cnt<=n&&cnt+ppp>=n)
    61         // 如果cnt<=n<=cnt+ppp,就说明这个假设合理 
    62         //如果出现Impossible的情况,怎么枚举都会err,这里直接跳过就行了 
    63             if(nx&&nx!=px){
    64                 //出现了两个合理的罪犯 
    65                 cout<<"Cannot Determine";
    66                 return 0;
    67             }else nx=px;
    68     }
    69     if(!nx)cout<<"Impossible"; 
    70     else cout<<name[nx];
    71 }
  • 相关阅读:
    [译]C++如何切分字符串
    Reverse Words in a String
    excel 获取中文拼音首字母
    jquery 获取css position的值
    excel 截取单元格部分内容(从指定位置截取)
    excel 根据单元格内容自动调整列宽
    excel 如何为列添加指定内容(字符串)
    android_handler(二)
    &quot;Simple Factory&quot; vs &quot;Factory Method&quot; vs &quot;Abstract Factory&quot; vs &quot;Reflect&quot;
    HDU 5074 Hatsune Miku 2014 Asia AnShan Regional Contest dp(水
  • 原文地址:https://www.cnblogs.com/jiamian/p/11230623.html
Copyright © 2020-2023  润新知