1. 程式人生 > >CF49E Common ancestor(dp+dp+dp)

CF49E Common ancestor(dp+dp+dp)

紀念卡常把自己卡死的一次自閉模擬賽

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;
}