区间dp
思路挺好想的,不过实现......极其鬼畜
令dp[i][j][k]表示i ->j区间能否合为k,pan[b][c]表示b,c字母能合并为哪些字母。
转移方程:$$dp[l][r][a]=dp[l][r][a] | (dp[l][k][b]cap dp[k+1][r][c]) Kepsilon [l,r],pan[b][c]==a$$
code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#define max(a,b) a>b?a:b
#define maxn 102
using namespace std;
int w,ii,n,g;
bool dp[206][206][7],t[7][17][17];
int it[7][18][5],num[17];
char str[206];
inline int make(char tmp)
{
if (tmp=='W') return 1;
if (tmp=='I') return 2;
if (tmp=='N') return 3;
if (tmp=='G') return 4;
return 0;
}
int main()
{
cin>>w>>ii>>n>>g;
for (int i=1;i<=w;++i)
{
char tm[5];
scanf("%s",tm+1);
t[1][make(tm[1])][make(tm[2])]=1;
it[1][++num[1]][1]=make(tm[1]),it[1][num[1]][2]=make(tm[2]);
}
for (int i=1;i<=ii;++i)
{
char tm[5];
scanf("%s",tm+1);
t[2][make(tm[1])][make(tm[2])]=1;
it[2][++num[2]][1]=make(tm[1]),it[2][num[2]][2]=make(tm[2]);
}
for (int i=1;i<=n;++i)
{
char tm[5];
scanf("%s",tm+1);
t[3][make(tm[1])][make(tm[2])]=1;
it[3][++num[3]][1]=make(tm[1]),it[3][num[3]][2]=make(tm[2]);
}
for (int i=1;i<=g;++i)
{
char tm[5];
scanf("%s",tm+1);
t[4][make(tm[1])][make(tm[2])]=1;
it[4][++num[4]][1]=make(tm[1]),it[4][num[4]][2]=make(tm[2]);
}
scanf("%s",str+1);
int len=strlen(str+1);
for (int i=1;i<=len;++i)
{
dp[i][i][make(str[i])]=1;
}
for (int i=2;i<=len;++i)
for (int j=1;j<=len-i+1;++j)
{
int k=j+i-1;
for (int l=j;l<=k;++l)
for (int m=1;m<=4;++m)
for (int o=1;o<=num[m];++o)
dp[j][k][m]|=(dp[j][l][it[m][o][1]] & dp[l+1][k][it[m][o][2]]);
}
int ans[5];
ans[1]=ans[2]=ans[3]=ans[4]=0;
for (int i=1;i<=4;++i)
{
if (dp[1][len][i]) ans[i]=1;
}
if (!ans[1]&&!ans[2]&&!ans[3]&&!ans[4]) cout<<"The name is wrong!";
else
{
if (ans[1]) cout<<"W";
if (ans[2]) cout<<"I";
if (ans[3]) cout<<"N";
if (ans[4]) cout<<"G";
}
return 0;
}
收获:大力dp就好了