Codeforces 156B. Suspects

题目大意:

福尔摩斯正在处理一件案子。此时已经抓捕了n个嫌疑人,里面只可能有一个是真正的犯人。福尔摩斯正在审问这些嫌疑人。每个嫌疑人的回答只有两种,一种表明他说编号为i的嫌疑人不是犯人,用-i表示;另一种表明他说编号为i的嫌疑人是犯人,用+i表示。聪明的福尔摩斯已经知道了其中有m个人说的是真话。要求那些人说的是真话,那些人说的是假话。

做法:

这的确是个很有意思的题啊。但是放在这里的话,我们就不能还想以前一样,只用严谨的逻辑推理去得出答案(或者有大牛真的能这样。。。),我们利用计算机的话,就可以暴力的去判断哪些人说的一定是真话,假话,或者不确定。
很简单,我们假设第i号嫌疑人是犯人,那么我们 扫描一遍所有嫌疑人说的话,就可以知道mi个人说了真话,如果mi恰好等于m,那么将i号嫌疑犯记录到ans数组(记录可能为犯人的人的数组)中。当i从0到n扫完之后,如果ans数组里面只有一个元素,那么说明记录的这个嫌疑犯就是真正的犯人,那么就能确定哪些人说的是真话,哪些人说的是假话。
如果ans数组里面有很多元素,那么也能确定哪些人说的一定是真话,哪些人说的是假话,哪些人说的话是不确定的。

这样看来,我们的复杂度是O(n^2)咯?这不超时了吗?。。。难道要重新想方法?。。
其实不用,上文中描红加粗的部分其实不用每次都扫一遍数组,我们可以再输出的时候,维护一个a[i](代表说i是犯人的有多少人),一个b[i](代表说i不是犯人的有多少人)就行,那么前面说的mi就可以这样计算: mi=a[i]+bn-b[i], 其中bn为说某人不是犯人的人的总数。这样的话,上述遍历的过程就可以变为O(1)时间完成,那这样就可以过啦~

代码:
#include <iostream>
#include <cstdio>
#define N 100010
using namespace std;
int a[N],b[N],ans[N],say[N],numa,numb,numans;
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=0;i<n;i++)
    {
        scanf("%d",&say[i]);
        if(say[i]>0) numa++,a[say[i]]++;
        else numb++,b[-say[i]]++;
    }
    for(int i=1;i<=n;i++)
    {
        if(a[i]+numb-b[i]==m) ans[i]=1,numans++;
    }
    for(int i=0;i<n;i++)
    {
        if(say[i]>0 && !ans[say[i]]) cout<<"Lie"<<endl;
        else if(say[i]>0 && ans[say[i]] && numans==1) cout<<"Truth"<<endl;
        else if(say[i]>0 && ans[say[i]]) cout<<"Not defined"<<endl;
        else if(say[i]<0 && ans[-say[i]] && numans==1) cout<<"Lie"<<endl;
        else if(say[i]<0 && ans[-say[i]]) cout<<"Not defined"<<endl;
        else if(say[i]<0 && !ans[-say[i]]) cout<<"Truth"<<endl;
    }
    return 0;
}



缩减版:
#include <cstdio>
const int N=100010;
int n,m,a[N],b[N],as[N],s[N],nb,nans;
int main(){
    scanf("%d%d",&n,&m);
    for(int i=0;i<n&&~scanf("%d",&s[i]);i++)
        if(s[i]>0) a[s[i]]++;
        else nb++,b[-s[i]]++;
    for(int i=1;i<=n;i++)if(a[i]+nb-b[i]==m) as[i]=1,nans++;
    nans=nans==1?1:0;
    for(int i=0;i<n;i++)
        if(s[i]>0) puts(!as[s[i]]?"Lie":nans?"Truth":"Not defined");
        else puts(!as[-s[i]]?"Truth":nans?"Lie":"Not defined");
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值