牛客小白月賽88-DE題解

ouhq發表於2024-03-13

D-我不是大富翁

題意:牛客小白月賽88-DE題解

做法:一開始是往貪心方面想,但是很明顯,貪不了。又因為走的步先後順序沒影響,可以用dp來寫。暴力也差不多。

值得注意的點是動力序列可以一邊讀入一邊處理,省了點空間。

如果dp[5005][5005]這樣開的話會MLE,實際上在dp的過程中,用到的只是i和i-1兩行,其餘都是多餘的。

所以可以這樣定義dp[2][5005]這樣可以避免MLE;

還有就是要特判n==1的情況。

//int dp[5005][5005];  //--MLE
int dp[2][5005];  //最佳化空間!!!                  wa--5 1 0
//dp[i][j]定義為,到了第i步,可以到達j哪些格子。
//暴力差不多--遍歷m步.每步遍歷n,看看可以到達哪些格子.看看最後一步是否可以到達1.

void solve(){           //D
    int n,m,x;
    cin>>n>>m;
    if(n==1){               //特判!!
        cout<<"YES";
        return;
    }
    dp[0][1]=1;
    for(int i=1;i<=m;i++){
        cin>>x;
        x%=n;
        for(int j=1;j<=n;j++){
            if(dp[(i+1)%2][j]){
                    dp[(i+1)%2][j]=0;      //重置!!!否則下一步會再次進入
                    dp[i%2][(j+x)%n]=1;
                    dp[i%2][(j-x+n)%n]=1;
            }
        }
    }
    if(dp[m%2][1]) cout<<"YES";
    else cout<<"NO";
}

在寫的時候加了個條件判斷x==0的情況,但是實際上x==0的情況是相同處理的,不用特別處理。

E-多重對映

題意:牛客小白月賽88-DE題解牛客小白月賽88-DE題解

做法有兩種。

法一:順著做,並查集(維護集合的關係,不是值的關係),加一點其他輔助。設定兩個unordered_map<int,int>:mp1[集合]=值 ,mp2[值]=集合。

int fa[1000006];            //fa存的是各個集合,不是值!!
int find(int x){
    if(fa[x]==x) return x;
    return fa[x]=find(fa[x]);
}
void Union(int x,int y){
    int fa1=find(x),fa2=find(y);
    fa[fa1]=fa2;
}
//這題順著做,對映關係有點繞,要想清楚對映關係!!
void solve(){       //E多重對映--①多重對映 or ②正難則反     有T=1e5組樣例,ai最大又有1e6。如果每個樣例都初始化到1e6--TLE--solve--輸入哪個數字初始化哪個數字。
    int n,m,arr[100005];
    cin>>n>>m;
    map<int,int> mp1,mp2;    //key!! mp1[集合]=值 : mp2[值]=集合
    for(int i=1;i<=n;i++){
        cin>>arr[i];
        fa[arr[i]]=arr[i];
        mp1[arr[i]]=arr[i];
        mp2[arr[i]]=arr[i];
    }
    for(int i=1;i<=m;i++){
        int x,y;
        cin>>x>>y;
        if(!mp2[x]||x==y) continue;  //x不存在於集合
        if(!mp2[y]){                //如果y不存在於集合
            mp2[y]=mp2[x];          //y存在於x的集合
            mp1[mp2[x]]=y;          //x的集合對應的值為y
            mp2.erase(x);           //刪除x,x不存在於集合
        }
        else{
            Union(mp2[x],mp2[y]);   //集合的Union!!
            //x集合已經融入到y集合,所以要先刪除x集合對應的值,並且刪除x集合。
            mp1.erase(mp2[x]);
            mp2.erase(x);
        }
    }
    for(int i=1;i<=n;i++) cout<<mp1[find(arr[i])]<<" ";
    cout<<endl;
}

法二:巧妙,正難則反。倒著遍歷操作。

int fa[1000006];
void solve(){           //E-法二-正難則反
    int n,m,arr[100005];
    cin>>n>>m;
    for(int i=1;i<=n;i++) {
        cin>>arr[i];
        fa[arr[i]]=arr[i];   //!!!
    }
    vector<pair<int,int>> vct;
    for(int i=1;i<=m;i++){
        pair<int,int> x;
        cin>>x.first>>x.second;
        vct.emplace_back(x);
        fa[x.first]=x.first;            //!!!所有出現的數字都要初始化為自己
        fa[x.second]=x.second;
    }
    for(int i=m-1;i>=0;i--){
        auto [x,y]=vct[i];
        fa[x]=fa[y];                    //!!!!!!
    }
    for(int i=1;i<=n;i++){
        cout<<fa[arr[i]]<<" ";
    }
    cout<<endl;
}

牛客小白月賽88-DE題解