[HAOI2008]玩具取名

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld

题目描述

某人有一套玩具,并想法给玩具命名。首先他选择WING四个字母中的任意一个字母作为玩具的基本名字。然后他会根据自己的喜好,将名字中任意一个字母用“WING”中任意两个字母代替,使得自己的名字能够扩充得很长。
现在,他想请你猜猜某一个很长的名字,最初可能是由哪几个字母变形过来的。 输入描述:
第一行四个整数W、I、N、G。表示每一个字母能由几种两个字母所替代。 接下来W行,每行两个字母,表示W可以用这两个字母替代。
接下来I行,每行两个字母,表示I可以用这两个字母替代。 接下来N行,每行两个字母,表示N可以用这两个字母替代。
接下来G行,每行两个字母,表示G可以用这两个字母替代。 最后一行一个长度不超过Len的字符串。表示这个玩具的名字。

输出描述:

一行字符串,该名字可能由哪些字母变形而得到。(按照WING的顺序输出) 如果给的名字不能由任何一个字母变形而得到则输出“The name
is wrong!”

示例1
输入

1 1 1 1
II
WW
WW
IG
IIII

输出

IN

题解

题目虽然输入的是字母,但是我们可以当做数字来处理
重要的两个数组,均为bool型
dp[i][j][k]表示区间[i,j]是否是由k转化而来的
can[i][j][k]表示i是否可以用 j k 两个字母代替
can是在读入时更新
我们想一想区间更新的条件:
区间[l,r],中间点为k
z,z1,z2为1~4,表示为WING
如果左区间[l,k+1]可以由z1转化(即代码dp[l][k][z1]),右区间[k+1,r]可以由z2转化(代码dp[k+1][r][z2]),
z可以用z1和z2来代替(代码dp[z][z1][z2]),那是不是就说明区间[l,r]是由z转化的
(相当于z1,z2是一个中转站,用来将区间[l,r]与z联系在一起)

套上区间dp的万能模板
就能得到

for(int i=1;i<=len;i++) dp[i][i][a[i]]=true;//从i到i可以由他自己转化
    
    for(int led=1;led<len;led++)//列举长度的可能性
        for(int l=1;l<=len-led;l++)//列举左界的可能性 
		{
   
            int r=l+led;//右界可以算出来
            for(int k=l;k<r;k++) //在左右之间列举可能的断点 
                for(int z=1;z<=4;z++)  //列举l到r(需要的区间)可能转化成的字母
                    for(int z1=1;z1<=4;z1++) // 列举l到k(借助的左区间)
                        for(int z2=1;z2<=4;z2++)  //列举k + 1到r(借助的右区间)
                            if(can[z][z1][z2] && dp[l][k][z1] && dp[k+1][r][z2]) 
//如果z1、z2可以转化成z 并且 l到k可以变成z1 并且k+1到r可以变成z2
                                dp[l][r][z]=true;
//那么l到r就可以转化成z
        }

代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=3e2+7;
int dp[maxn][maxn][maxn];
bool can[maxn][maxn][maxn];
int p[maxn];
char s[maxn];
int a[300];
int change(char i){
     
    if(i=='W') return 1; 
    if(i=='I') return 2;
    if(i=='N') return 3; 
    if(i=='G') return 4; 
} 
int main()
{
   
	a['W']=1;
	a['I']=2;
	a['N']=3;
	a['G']=4;
	for(int i=1;i<=4;i++)cin>>p[i];
	for(int i=1;i<=4;i++)
	{
   
		for(int j=1;j<=p[i];j++)
		{
   
			char x,y;
			cin>>x>>y;
			can[i][a[x]][a[y]]=1;
		}
	}
	cin>>(s+1);
	int n=strlen(s+1);
// cout<<n<<endl; 
	for(int i=1;i<=n;i++)dp[i][i][a[s[i]]]=1;
	for(int len=2;len<=n;len++)
	{
   
		for(int i=1;i<=n;i++)
		{
   
			int j=i+len-1;
			if(j>n)break;
			for(int k=i;k<=j;k++)
			{
   
				for(int z=1;z<=4;z++)
				{
   
					for(int z1=1;z1<=4;z1++)
					{
   
						for(int z2=1;z2<=4;z2++)
						{
   
							if(can[z][z1][z2]&&dp[i][k][z1]&&dp[k+1][j][z2])
							dp[i][j][z]=1;
						}
					}
				}
			}
			
		}
	}
	bool flag=false;
    if(dp[1][n][1]) {
   flag=true;printf("W");}
    if(dp[1][n][2]) {
   flag=true;printf("I");}
    if(dp[1][n][3]) {
   flag=true;printf("N");}
    if(dp[1][n][4]) {
   flag=true;printf("G");}
    if(!flag) printf("The name is wrong!");
}