洛谷 P1039侦探推理

/*
枚举罪犯和星期几,那么所有人说的话是真是假一目了然。
首先一个人不能既说真话又说假话。
即:
I am guilty.
I am not guilty.
因为非真即假,所以直接判断impossible.
map<string,int>表示名字对应的id  name[i]表示id为i的人对应的人名。 
kp[i][j]表示第i个人认为第j个人是不是罪犯。0/-1/1,表示不是,没说,是 
kd[i][j]表示第i个人认为今天是星期j么?0/-1/1表示 不认为、没说、认为 
impossible的情况之一即kp[i][j]=1后又要赋值为 kp[i][j]=0;


假设罪犯是t(id),今天是d;//现在已经没有人既说真话又说假话了。

现在人有三类:
说真话 说假话 说废话 
如何判断一个人说真话还是说假话。
假设
Amy: 
①I am guilty. (如果Amy不是t,那么这人说假话;如果Amy是t,那么这个人一定说真话)
②I am not guilty. (如果他是t,那么这人说假话;如果不是t,那么它一定说真话) 
③XXX is guilty. (XXX是t,真话;XXX不是t,假话)
④XXX is not guilty. (XXX是t,假话;XXX不是t,真话) 
⑤Today is x  (x是d,真话;x不是d,假话)。

所以,一个人只要不说废话,那么他说真话还是说假话都知道了 
say[i]=0/-1/1表示id为i的人说假话,不确定,说真话 

若一个人说的是假话,那么他说的假话的反面就是真话。 
下面的句子是可以通过这个人说的是假话判断出来的。 
①他不是罪犯 
②他是罪犯
③XXX 不是罪犯
④XXX 是罪犯 
这时需要另辟数组来标记。
p[i]=0/-1/1分别表示这个人不是罪犯、不确定、是罪犯。
d[i]=0/-1/1分别表示今天不是星期i、不确定是不是星期i、今天是星期i。

这时矛盾出现了

甲:
A is guilty.
t is not guilty.
乙:
A is not guilty.
t is not guilty.

此时甲、乙二人都不认为t是罪犯,那么他们说的都是假话了
根据甲来说:A 应该不是罪犯。
根据乙来说:A 应该是罪犯。
出现了矛盾,这个情况应该pass了。
即p[i]赋值为0后,又要被赋值为1。d数组同样。
所以p[]、d[]数组只能从不确定状态赋值为确定状态,不能从一个
确定状态标记为另一个确定状态。

如果说谎话的人<=n&&说谎的人+说废话的人>=n,那么满足始终有n个人说假话了。

在看p[i]数组,如果有人标记了不确定是不是罪犯,那么这个情况就是不确定罪犯了。

所以最后
1)若唯一能确定罪犯,输出。
2)不能唯一确定,有多解的情况 cannot determine。
3)所有举例的情况都有矛盾,都被pass了,impossible.

下面的代码windows下是A的...洛谷A不了....占个坑。

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

int m,n,p,ans;

int say[22];

int kp[22][22],kd[22][10]; 

string name[22];

string a[22],b[22],c[22];

map<string,int>k;

void read()
{
    for(int i=1;i<=m;i++)
    {
        string s;
        cin>>s; name[i]=s;
        s+=":"; k[s]=i;
    }
//    char ch=getchar();
   // while(ch==10)ch=getchar();getchar();
    for(int i=1;i<=p;i++)
    {
        cin>>a[i]>>b[i];
        if(b[i]!="Today"&&c[i]!=" is guilty."&&c[i]!=" is not guilty.")b[i]+=":";//***
        getline(cin,c[i]);
    }
}

void pre_deal()
{
    int flag=0;
    memset(kp,-1,sizeof(kp));
    memset(kd,-1,sizeof(kd));
    for(int i=1;i<=p;i++)
    {
        int id=k[a[i]];
        if(c[i]==" am guilty.") {if(!kp[id][id]) flag=1;else kp[id][id]=1;}
        if(c[i]==" am not guilty."){if(kp[id][id]==1) flag=1;else kp[id][id]=0;}
        if(c[i]==" is guilty.") {int h=k[b[i]];if(!kp[id][h]) flag=1;else kp[id][h]=1;}
        if(c[i]==" is not guilty."){int h=k[b[i]];if(kp[id][h]==1) flag=1;else kp[id][h]=0;}
        if(b[i]=="Today")
        {
            if(c[i]==" is Monday.") kd[id][1]=1;
            if(c[i]==" is Tuesday.") kd[id][2]=1;
            if(c[i]==" is Wednesday.") kd[id][3]=1;
            if(c[i]==" is Thursday.") kd[id][4]=1;
            if(c[i]==" is Friday.") kd[id][5]=1;
            if(c[i]==" is Saturday.") kd[id][6]=1;
            if(c[i]==" is Sunday.") kd[id][7]=1;
        }
        if(flag){printf("Impossible
");exit(0);}  //***
    }
}

bool check(int x,int y)
{
    memset(say,-1,sizeof(say));
    for(int i=1;i<=m;i++)
    {
        for(int j=1;j<=m;j++)
        {
            if((kp[i][j]==1&&j!=x)||(kp[i][j]==0&&j==x))
            {
                if(say[i]==1) return false; 
                say[i]=0; 
            }
            if((kp[i][j]==1&&j==x)||(kp[i][j]==0&&j!=x))
            {
                if(say[i]==0) return false;
                say[i]=true; 
            }
        }
        for(int q=1;q<=7;q++)
        {
            if(kd[i][q]==1&&q!=y)
            {
                if(say[i]==1) return false;
                say[i]=0;
            }
            if(kd[i][q]==1&&q==y)
            {
                if(say[i]==0) return false;
                say[i]=true;
            }
        } 
    }
    return true;
}

int main()
{
    scanf("%d%d%d",&m,&n,&p);//m同学数,n始终说谎的人数,p为证言总数。
    read(); 
    pre_deal();
    for(int i=1;i<=m;i++)
    {
        for(int j=1;j<=7;j++)
        {
            int saylie=0,notjudge=0;
            if(check(i,j))
            {
                for(int p=1;p<=m;p++)
                {
                    if(say[p]==-1) notjudge++;
                    if(say[p]==0) saylie++;
                }
                if(saylie<=n&&notjudge+saylie>=n)
                {
                    if(ans&&i!=ans)
                    {
                        printf("Cannot Determine
");
                        exit(0);
                    }
                    ans=i;
                }
            }
        }
    }
    if(ans) cout<<name[ans]<<endl;
    else printf("Impossible
");
    return 0;
}
原文地址:https://www.cnblogs.com/zzyh/p/9539107.html