[NOIP2003]逻辑推理

这道题我弄了一下午。。。

说说解题思路吧:

(1)限定输入。先把主语和证词分离开,如果证词是“Today is XXX.”,无非7种情况,比对一下就可以了,存进结构体。如果不是,区分开证词的主语和实际证词。实际证词也只有4种情况:“am guilty.”“am not guilty.”“is guilty.”“is not guilty.”,前两种情况要求证词的主语是“I”。比对一下,也存进结构体。我用的是外链链表,方便检查是否合法。

(2)DFS哪些人说谎,把他们的证词反过来储存。我用了is这个布尔变量来记录,is=true表示这句话说的是Today is XXX或者XXX is/am guilty,is=false表示这句话说的是Today is not XXX或者XXX is/am not guilty。例如,若A的某句证词是"B is guilty."。倘若A说假话,则他的证词反过来后,就应该按照“B is not guilty.”来储存。之后把这些新的证词存到另一个数组中。依次检查这些证词,遇到前后矛盾的就回溯。为了状态判重,我用了个Trie....

(3)最后处理一下:

设cnt1为一定是罪犯的人数,cnt2为一定不是罪犯的人数,cnt3为可能是罪犯也可能不是的人数。

按顺序(注意!)检查以下几项

①如果所有情况下都出现了前后矛盾,输出Impossible。

②如果cnt2==N,输出Impossible。

③如果cnt1==1,直接输出罪犯的名字。

④如果cnt2==N-1,输出剩下那个人的名字。

⑤如果cnt3>0,或者其他情况,输出Cannot Determine.


就是这些了,编程难度不高但是很烦,尤其是(3)我搞了好久WA了几次才对。。。


RQNOJ AC

 

#include <cstdio>
#include <cstring>
#include <map>
#include <iostream>

using namespace std;

struct proof
{
       bool is;
       int from , obj;
       int wd;
       proof* next;
}*pr[20] , *chk[20];
map<string , int> name;
map<int , string> re_name;
short may[21] , srch[21];
int n , m , _p , p;
bool zxmd = true;

struct tr_node
{
       bool flag;
       int next[2];
       tr_node()
       {
                next[0] = next[1] = flag = 0;
       }
}trie[1 << 20 + 1];
int trie_size = 0;

#define ts trie_size

void ins_trie(int loc , int th)
{
     if (loc == n)
     {
         trie[th].flag = true;
         return;
     }
     short x = srch[loc];
     if (trie[th].next[x] != 0)
         ins_trie(loc + 1 , trie[th].next[x]);
     else
     {
         trie[th].next[x] = ++ts;
         ins_trie(loc + 1 , ts);
     }
}
bool find_trie(int loc , int th)
{
     if (loc == n)
         return trie[th].flag;
     short x = srch[loc];
     if (trie[th].next[x] != 0)
         return find_trie(loc + 1 , trie[th].next[x]);
     else
         return false;
}

void set_proof(int _from , int _obj , bool isornot , int weekday)
{
     proof *x = new proof;
     x->from = _from;
     x->obj = _obj;
     x->is = isornot;
     x->wd = weekday;
     x->next = pr[_from]->next;
     pr[_from]->next = x;
}
void cpy_proof(int _from , int _obj , bool isornot , int weekday)
{
     proof *x = new proof;
     x->from = _from;
     x->obj = _obj;
     x->is = isornot;
     x->wd = weekday;
     x->next = chk[_from]->next;
     chk[_from]->next = x;
}
bool inp(string x)
{
     int i , l = x.size();
     for (i = 0 ; i < l ; i++)
         if (x[i] == ':')
             break;
     
     string NAME(x) , PROOF(x , i + 2);
     NAME.erase(i , l - i);
     int man = name[NAME];
     if (man == 0) return false;
     
     l = PROOF.size();
     
     //Weekdays
     if (PROOF == "Today is Monday.")    {set_proof(man , 0 , true , 1); return true;}
     if (PROOF == "Today is Tuesday.")   {set_proof(man , 0 , true , 2); return true;}
     if (PROOF == "Today is Wednesday.") {set_proof(man , 0 , true , 3); return true;}
     if (PROOF == "Today is Thursday.")  {set_proof(man , 0 , true , 4); return true;}
     if (PROOF == "Today is Friday.")    {set_proof(man , 0 , true , 5); return true;}
     if (PROOF == "Today is Saturday.")  {set_proof(man , 0 , true , 6); return true;}
     if (PROOF == "Today is Sunday.")    {set_proof(man , 0 , true , 7); return true;}
     
     //Who is guilty
     for (i = 0 ; i < l ; i++)
         if (PROOF[i] == ' ')
             break;
     string OBJ(PROOF) , INST(PROOF , i + 1);
     OBJ.erase(i , l - i);
     if (INST == "am guilty." && OBJ == "I")
     {
              set_proof(man , man , true , 0);
              return true;
     }
     if (INST == "am not guilty." && OBJ == "I")
     {
              set_proof(man , man , false , 0);
              return true;
     }
     if (INST == "is guilty.")
     {
              set_proof(man , name[OBJ] , true , 0);
              return true;
     }
     if (INST == "is not guilty.")
     {
              set_proof(man , name[OBJ] , false , 0);
              return true;
     }
     return false;
}

short gor(short a , short b)
{
      if (!a) return b;
      if (!b) return a;
      if ((!a) && (!b)) return 0;
      if (!(a + b)) return 2;
      if (a == b) return a;
}
void RES_chk()
{
     for (int i = 1 ; i <= n ; i++)
     {
         chk[i] = new proof;
         chk[i]->next = NULL;
     }
}
void check()
{
     short g[21] = {0} , _wd = -1;
     if (find_trie(1 , 0))
         return;
     ins_trie(1 , 0);
     
     RES_chk();
     for (int i = 1 ; i <= n ; i++)
     {
         proof *P = pr[i];
         P = P -> next;
         while (P != NULL)
         {
               bool x = P->is;
               if (srch[i])
                   x = !x;
               cpy_proof(P->from , P->obj , x , P->wd);
               P = P->next;
         }
     }
     for (int i = 1 ; i <= n ; i++)
     {
         proof *P = chk[i];
         P = P -> next;
         while (P != NULL)
         {
               if (P->is == true && !P->wd)
               {
                   int Obj = P->obj;
                   if (g[Obj] == 0)
                       g[Obj] = 1;
                   if (g[Obj] == -1)
                       return;
               }
               if (P->is == false && !P->wd)
               {
                   int Obj = P->obj;
                   if (g[Obj] == 0)
                       g[Obj] = -1;
                   if (g[Obj] == 1)
                       return;
               }
               if (P->is == true && P->wd > 0)
               {
                   if (_wd == -1)
                       _wd = P->wd;
                   if (_wd != P->wd)
                       return;
               }
               if (P->is == false && P->wd > 0)
                   if (P->wd == _wd)
                       return;
               P = P->next;
         }
     }
     for (int i = 1 ; i <= n ; i++)
         may[i] = gor(may[i] , g[i]);
     zxmd = false;
}
void dfs(int loc , int count)
{
     if (loc == n + 1 && count < m)
         return;
     if (count > m)
         return;
     if (count == m)
     {
         check();
         return;
     }
     srch[loc] = 1;
     dfs(loc + 1 , count + 1);
     srch[loc] = 0;
     dfs(loc + 1 , count);
}

int main()
{
    //freopen("logic.in" , "r" , stdin);
    //freopen("logic.out" , "w" , stdout);
    cin >> n >> m >> _p;
    for (int i = 1 ; i <= n ; i++)
    {
        pr[i] = new proof;
        pr[i]->next = NULL;
    }
    for (int i = 1 ; i <= n ; i++)
    {
        string x;
        cin >> x;
        name[x] = i;
        re_name[i] = x;
    }
    getchar();
    for (int i = 1 ; i <= _p ; i++)
    {
        char s[256];
        cin.getline(s , 255);
        string x(s);
        if (inp(x))
            p++;
    }
    
    dfs(1 , 0);
    
    int guilty = 0 , cnt1 = 0 , cnt2 = 0 , cnt3 = 0;
    for (int i = 1 ; i <= n ; i++)
    {
        if (may[i] == 1)
        {
            guilty = i;
            cnt1++;
        }
        if (may[i] == -1)
            cnt2++;
        if (may[i] == 2)
            cnt3++;
    }
    
    if (zxmd) cout << "Impossible" << endl;
    else if (cnt2 == n) cout << "Impossible" << endl;
    else if (cnt1 == 1) cout << re_name[guilty] << endl;
    else if (cnt2 == n - 1)
    {
         guilty = 0;
         for (int i = 1 ; i <= n ; i++)
             if (may[i] == 0)
             {
                 guilty = i;
                 break;
             }
         cout << re_name[guilty] << endl;
    }
    else if (cnt3 > 0) cout << "Cannot Determine" << endl;
    else cout << "Cannot Determine" << endl;
    
    return 0;
}




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值