1. 程式人生 > 實用技巧 >狀壓dp 圖論 難題 E. String Transformation 2

狀壓dp 圖論 難題 E. String Transformation 2

狀壓dp 圖論 難題 E. String Transformation 2

題目大意:

你有兩個串, \(A\)\(B\) ,你可以對A進行操作使得 \(A=B\)

操作:選擇k個相同的字元,然後把他們修改成相同的字元。

題目保證 \(A\) \(B\) 只有字母表的前二十個字元。

題解:

這個題目確實很難,不過劉雨小姐姐也是真的強,把這個題目轉化成求最大的有向無環圖。

這個題目的題解答案就是 :\(2*n-S-1\) 這個\(S\) 就是最大有向無環圖的點的數量。

至於為什麼是這個答案呢?

假設 \(A\) 變成 \(B\) 是一個有向無環圖,那麼就是說和版本1是一樣的。

那麼對這個圖示一個優先順序,讓優先順序低的往優先順序高的跳。

注意這個點的3和4的優先順序無所謂,但是標4為3,那麼5一定是4。

很容易發現答案就是這個有向無環圖的點的數量-1,就是要跳的次數,因為版本1規定了跳的方向,一定是往字元大的方向跳,所以不會形成環,就是一個有向無環圖,那麼如果加入了一個點,使得這個圖出現環了呢?

這樣的話,是不是最多就要多跳兩次。

因為這個點一定要跳出去,所以一次,別人一定要跳到它,所以一次,至少也要兩次,所以加入的這個點如果構成環了,那麼會多跳兩次。

本來是每一個點的貢獻都是2,但是形成有向無環圖之後它只要跳出去一次就一定可以到達終點,所以會減少一次,對於終點1,就只要0次,減少了兩次。

所以最後答案就是 :\(2*n-S-X\)

其中 \(S\) 表示所有獨立的最大有向無環圖的點數相加,\(X\) 表示獨立的有向無環圖的數量。

最後怎麼求最大的有向無環圖呢,這個可以狀壓求。

首先預處理一下每一個點指向的位置,定義\(dp[s]\) 表示狀態 \(s\) 下,是否為一個有向無環圖,每一個點加入這個狀態的要求它指向的點都不在狀態 \(s\) 中,那麼就可以加入。

判斷圖的數量可以用並查集。

#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
#define inf64 0x3f3f3f3f3f3f3f3f
using namespace std;
const int maxn = 1e5+10;
char A[maxn],B[maxn];
int f[30],to[30];
int findx(int x){
    return f[x]==x?x:f[x]=findx(f[x]);
}
void unite(int x,int y){
    x = findx(x);
    y = findx(y);
    if(x==y) return ;
    f[x]=y;
}
bool dp[1<<21];
int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        int n;
        scanf("%d",&n);
        scanf("%s%s",A+1,B+1);
        for(int i=0;i<20;i++) f[i]=i,to[i]=0;
        for(int i=1;i<=n;i++){
            int x = A[i]-'a', y = B[i]-'a';
            to[x]|=(1<<y);
            unite(x,y);
        }
        int ans = 40;
        for(int i=0;i<20;i++) if(findx(i)==i) ans--;
        memset(dp,false,sizeof(dp));
        dp[0]=true;
        int maxs = 0;
        for(int s=0;s<(1<<20);s++){
            if(!dp[s]) continue;
            int cur = 0;
            for(int i=0;i<20;i++){
            	int tmp = 1<<i;
            	if(tmp&s) cur++;
                if(to[i]&s) continue;
                dp[s|tmp]=true;
            }
            maxs=max(maxs,cur);
        }
        printf("%d\n",ans-maxs);
    }
    return 0;
}

/*
2
4
cabc
abcb
 */