狀壓dp 圖論 難題 E. String Transformation 2
阿新 • • 發佈:2020-07-29
狀壓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\)
最後怎麼求最大的有向無環圖呢,這個可以狀壓求。
首先預處理一下每一個點指向的位置,定義\(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 */