洛谷P11361 [NOIP2024] 編輯字串

一位XXS發表於2024-12-01

Problem



Solve

首先任意更換相鄰元素任意次等同於在可交換範圍內隨便移動
這題是求最優解,直觀想到DP和貪心,但是容易反應過來本題DP的話很難做到無後效性,且狀態較多,故嘗試貪心
不難發現,我們從左往右遍歷的某個時刻進行交換後所得到的區域性最優解總是答案的一種方案的一部分
原因很簡單,我們不會因為我們為了讓當前的某一列相等而做出的排程導致更加壞的結果,我們所進行的排程都會做出貢獻,所以不管怎麼影響後面的操作,結果都不會比啥也不幹還壞
大概思路有了,現在來講一下實現:
把每個可以隨意交換的區間匯出來(兩列可以一起混在一個地方記錄),記錄其中1和0的個數,無需記錄範圍
匯出的過程中把每個格子所屬於的區間記下來,這樣就可以瞬間查詢所屬區間的0/1儲備情況
然後開始遍歷每一列,採取如下步驟:
如果這一列全都被固定,直接計算
如果有一列被固定,進行排程(比如當前格子(未固定)所屬區間編號為x,該去年當前與固定的格子相同的有f[x][0]個,那麼f[x][0]--)
如果都沒固定,看看什麼元素二者區間都有,然後進行排程,如果沒有就拉倒
排程成功或者匹配成功就ans++
注意,沒有固定的格子在匯出區間之後我們就不關心它具體的值了,我們可以認為它一定是排程來的(先假設在別的地方,再回來)

Code

#include<bits/stdc++.h>
using namespace std;
int T,n;
int f[2][100005],col;
string a,b,s1,s2;
struct p{
    int x,y;
    int sum(){
        return x+y;
    }
};
vector<p> g;
void input(){
    g.clear();
    g.push_back({-1,-1});
    col=1;
    cin>>n;
    getline(cin,a);
    getline(cin,a);
    getline(cin,b);
    getline(cin,s1);
    getline(cin,s2);
    a="#"+a;
    b="#"+b;
    s1="#"+s1;
    s2="#"+s2;
}
void solve1(){
    int cx=0,cy=0;
    for(int i=1;i<=n;i++){
        if(s1[i]=='1'){
            if(a[i]=='1')cx++;
            else cy++;
            f[0][i]=col;
        }else{
            if(cx||cy){
                g.push_back({cx,cy});
                col++;
                cx=cy=0;
            }
            f[0][i]=0;
        }
    }
    if(cx||cy){
        g.push_back({cx,cy});
        col++;
        cx=cy=0;
    }
    for(int i=1;i<=n;i++){
        if(s2[i]=='1'){
            if(b[i]=='1')cx++;
            else cy++;
            f[1][i]=col;
        }else{
            if(cx||cy){
                g.push_back({cx,cy});
                col++;
                cx=cy=0;
            }
            f[1][i]=0;
        }
    }
    if(cx||cy){
        g.push_back({cx,cy});
        col++;
        cx=cy=0;
    }
}
int solve2(){
    int ans=0;
    for(int i=1;i<=n;i++){
        if(!f[0][i]&&!f[1][i])ans+=(a[i]==b[i]);
        else if(!f[0][i]){
            if(a[i]=='1'){
                if(g[f[1][i]].x)g[f[1][i]].x--,ans++;
                else g[f[1][i]].y--;
            }else{
                if(g[f[1][i]].y)g[f[1][i]].y--,ans++;
                else g[f[1][i]].x--;
            }
        }else if(!f[1][i]){
            if(b[i]=='1'){
                if(g[f[0][i]].x)g[f[0][i]].x--,ans++;
                else g[f[0][i]].y--;
            }else{
                if(g[f[0][i]].y)g[f[0][i]].y--,ans++;
                else g[f[0][i]].x--;
            }
        }else{
            if(g[f[0][i]].x&&g[f[1][i]].x){
                g[f[0][i]].x--;
                g[f[1][i]].x--;
                ans++;
            }else if(g[f[0][i]].y&&g[f[1][i]].y){
                g[f[0][i]].y--;
                g[f[1][i]].y--;
                ans++;
            }else{
                if(g[f[0][i]].x){
                    g[f[0][i]].x--;
                    g[f[1][i]].y--;
                }else{
                    g[f[0][i]].y--;
                    g[f[1][i]].x--;
                }
            }
        }
    }
    return ans;
}
int main(){
    cin>>T;
    while(T--){
        input();
        solve1();
        cout<<solve2()<<endl;
    }
    return 0;
}