CF49E Common ancestor(dp+dp+dp)
阿新 • • 發佈:2018-12-22
紀念卡常把自己卡死的一次自閉模擬賽
QWQ
一開始看這個題,以為是個圖論,仔細一想,貌似可以直接dp啊。
首先,因為規則只有從兩個變為1個,貌似可以用類似區間\(dp\)的方式來\(check\)一段區間能不能合成某一個字母!
那我們定義\(f[i][j][k]\)表示第一個串,\([l,r]\)區間,是否可以合成\(k\)這個字母
然後轉移的時候,列舉區間,列舉規則,列舉斷點,滿足\(f[l][k][p1]==1\)且\(f[k+1][r][p2]==1\) 才能使當前狀態合法。
其中\(p1,p2\)表示當前規則的兩個字母
for (int i=1;i<=n;i++) f[i][i][cc(s[i])]=1; for (register int i=2;i<=n;++i) for (register int l=1;l<=n-i+1;++l) { int r = l+i-1; for (register int j=1;j<=26;++j) { for (register int p=1;p<=num[j];++p) { for (register int k=l;k<=r;++k) { f[l][r][j]=max(f[l][r][j],f[l][k][a[j][p].a]&f[k+1][r][a[j][p].b]); if (f[l][r][j]) break; } if (f[l][r][j]) break; } } }
同時定義\(g[l][r][k]\)陣列表示第二個串區間\([l,r]\)能否合成k。處理和f類似。
統計答案的時候呢
還需要一個\(dp[i][j]\)表示第一個串的前i個字元和第二個串的前j個字元的最短公共祖先
那麼,考慮列舉兩個斷點,兩個串的後面兩段能合成同一個字母,那麼就可以從那個斷點之前的狀態轉移過來
QWQ
詳細直接看程式碼吧
memset(dp,127/3,sizeof(dp)); dp[0][0]=0; for (register int i=1;i<=nn;++i) { for (register int k=1;k<=n;++k) { for (register int j=1;j<=i;++j) for (register int p=1;p<=k;++p) { if (dp[j-1][p-1]==dp[maxn-3][maxn-3]) continue; bool flag=false; for (register int o=1;o<=26;o++) if (g[j][i][o] && f[p][k][o]) flag=true; if (flag) dp[i][k]=min(dp[i][k],dp[j-1][p-1]+1); } } }
最後複雜度就是\(O(n^4*26)\)
我也不知道為啥能跑過啊
qwqwqwq
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<queue> #include<map> #include<set> #define mk makr_pair #define ll long long #include<ctime> using namespace std; inline int read() { int x=0,f=1;char ch=getchar(); while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();} while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();} return x*f; } const int maxn = 110; struct Node{ int a,b; }; Node a[maxn][maxn]; int num[maxn]; int f[maxn][maxn][maxn]; int g[maxn][maxn][maxn]; int n,m; char s[maxn]; char ss[maxn]; int nn; string ans; int dp[maxn][maxn]; inline int cc(char c) { return c-'a'+1; } int main() { scanf("%s",s+1); scanf("%s",ss+1); s[0]=ss[0]='*'; n=strlen(s+1); nn=strlen(ss+1); m=read(); for (register int i=1;i<=m;++i) { char ymh[10]; scanf("%s",ymh+1); int now = ymh[1]-'a'+1; num[now]++; a[now][num[now]].a = ymh[4]-'a'+1; a[now][num[now]].b = ymh[5]-'a'+1; } for (int i=1;i<=n;i++) f[i][i][cc(s[i])]=1; for (register int i=2;i<=n;++i) for (register int l=1;l<=n-i+1;++l) { int r = l+i-1; for (register int j=1;j<=26;++j) { for (register int p=1;p<=num[j];++p) { for (register int k=l;k<=r;++k) { f[l][r][j]=max(f[l][r][j],f[l][k][a[j][p].a]&f[k+1][r][a[j][p].b]); if (f[l][r][j]) break; } if (f[l][r][j]) break; } } } for (int i=1;i<=nn;i++) g[i][i][cc(ss[i])]=1; for (register int i=2;i<=nn;i++) for (register int l=1;l<=nn-i+1;++l) { int r = l+i-1; for (register int j=1;j<=26;++j) { for (register int p=1;p<=num[j];++p) { for (register int k=l;k<=r;++k) { g[l][r][j]=max(g[l][r][j],g[l][k][a[j][p].a]&g[k+1][r][a[j][p].b]); if (g[l][r][j]) break; } if (g[l][r][j]) break; } } } memset(dp,127/3,sizeof(dp)); dp[0][0]=0; for (register int i=1;i<=nn;++i) { for (register int k=1;k<=n;++k) { for (register int j=1;j<=i;++j) for (register int p=1;p<=k;++p) { if (dp[j-1][p-1]==dp[maxn-3][maxn-3]) continue; bool flag=false; for (register int o=1;o<=26;o++) if (g[j][i][o] && f[p][k][o]) flag=true; if (flag) dp[i][k]=min(dp[i][k],dp[j-1][p-1]+1); } } } if(dp[nn][n]==dp[maxn-3][maxn-3]) dp[nn][n]=-1; cout<<dp[nn][n]<<endl; return 0; }