洛谷P1039 [NOIP2003 提高組] 偵探推理

一位XXS發表於2024-12-02

Problem


Solve

較為快速且好想的暴力方法是列舉m個人中選n個的組合方案,然後對證詞進行檢驗,時間複雜度\(O(\frac{m!}{n!^2}p)\),仔細算算竟然能夠在2e8左右透過
但實際上這道題在當年肯定是給不了你2e8/sec的算力的,這道題目能夠評藍我覺得上面方法肯定是不配的
結合€€£在18年及以前的4e6/sec祖傳老爺機,我們需要換一種方法。
可以觀察到,我們在列舉組合的時候往往因為一個小小的改動而重新列舉p,這樣肯定不優,我們可以換種角度:
每次列舉真相可能的結果,將結果代入證詞進行檢驗,計算一下在這種情況有多少人說假話即可,時間複雜度\(O(dnp)\),d是星期的總數,為7
這樣就能過掉了,感覺根本沒有藍的難度。
PS:其實還有一種方法能把d最佳化掉,但是很麻煩,不建議寫(直接列舉兇手,根據初步得到的嫌疑人資訊推理可能的星期)

Code

#include<bits/stdc++.h>
using namespace std;
int n,m,p,w[105],f[105];//0:UB 1~7:Monday~Sunday 8/9: 2idx/2idx+1:
string name[25],ans;
map<string,int> b,day;
void init(){
    day["Monday."]=1,day["Tuesday."]=2,day["Wednesday."]=3,day["Thursday."]=4;
    day["Friday."]=5,day["Saturday."]=6,day["Sunday."]=7;
}
int main(){
    init();
    cin>>n>>m>>p;
    for(int i=1;i<=n;i++){
        cin>>name[i];
        b[name[i]]=i;
    }
    string tmp,t,word[255];
    int cnt=0;
    getline(cin,t);
    for(int i=1;i<=p;i++){
        getline(cin,t);
        t.erase(t.size()-1,1);//The problem's case was made in windows
        tmp="",t+=" ";
        cnt=0;
        for(int j=0;j<t.size();j++){
            if(t[j]!=' ')tmp+=t[j];
            else{
                word[++cnt]=tmp;
                tmp="";
            }
        }
        w[i]=b[word[1].substr(0,word[1].size()-1)];
        if(cnt==4){
            if(word[2]=="I"&&word[3]=="am"&&word[4]=="guilty."){
                f[i]=8;
            }
            if(word[3]=="is"&&word[4]=="guilty."){
                f[i]=10*b[word[2]];
            }
            if(word[2]=="Today"&&word[3]=="is"){
                f[i]=day[word[4]];
            }
        }else if(cnt==5){
            if(word[2]=="I"&&word[3]=="am"&&word[4]=="not"&&word[5]=="guilty."){
                f[i]=9;
            }
            if(word[3]=="is"&&word[4]=="not"&&word[5]=="guilty."){
                f[i]=10*b[word[2]]+1;
            }
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=7;j++){
            int cntf=0,cnth=0,c[25];
            memset(c,0,sizeof(c));
            for(int k=1;k<=p;k++){
                if(!f[k])continue;
                if(f[k]==j||f[k]==i*10||f[k]==8&&w[k]==i||f[k]==9&&w[k]!=i||f[k]>=10&&f[k]%10==1&&f[k]/10!=i){
                    if(c[w[k]]==-1){
                        cntf=0x3f3f3f3f;
                        break;
                    }
                    if(c[w[k]]==1){
                        continue;
                    }
                    cnth++;
                    c[w[k]]=1;
                }else{
                    if(c[w[k]]==1){
                        cntf=0x3f3f3f3f;
                        break;
                    }
                    if(c[w[k]]==-1){
                        continue;
                    }
                    cntf++;
                    c[w[k]]=-1;
                }
            }
            if(cntf<=m&&cntf+(n-cnth-cntf)>=m){
                if(ans==""){
                    ans=name[i];
                    break;
                }else{
                    cout<<"Cannot Determine";
                    return 0;
                }
            }
        }
    }
    if(ans=="")cout<<"Impossible";
    else cout<<ans;
    return 0;
}

相關文章