洛谷 4290 [HAOI2008]玩具取名 题解
P4290 [HAOI2008]玩具取名
题目描述
某人有一套玩具,并想法给玩具命名。首先他选择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
输出 #1
IN
说明/提示
30%数据满足Len<=20,W、I、N、G<=6
100%数据满足Len<=200,W、I、N、G<=16
【思路】
区间DP
从一串很长的字符串变为一个字母
从两个字母变回一个字母可以用一个映射来表示
先预处理一下每一次字符对应的映射
W = 1,I = 2,N = 3,G = 4
在代码中,就是你每一个数能够被替代的方式里面
第i个数到他的第j中 替代方法的映射,方便之后的转移
可以用f(i,j,k)
i表示是第i个数,j表示第i个数的第j中替代方法
然后k只有两个数1和2表示能够替代那一个字符的两个字符分别是什么
这样完成之后就可以开始DP了
dp(i,j,k)
i表示区间的左端点,j表示区间的右端点,k表示从i到j这一串字符串可以合并成的那一个字符
先初始化一下,将每一个从自己到自己能够合成的字符串初始化为自己所在位置上的字符
然后5重循环,枚举的内容在代码里面
最后检验从1到字符串最后,上面那四个数是不是又可以合并出来的
从第一个枚举到第四个一旦可以就输出
保证顺序是按照WING的
【完整代码】
#include<iostream>
#include<cstdio>
#include<map>
#include<cstring>
using namespace std;
int num[5];
map<char,int> w;
int f[5][20][3];
bool dp[205][205][5];
char a,b;
char s[205] = "0";
int len;
char ans[10] = {"0WING"};
int main()
{
w['W'] = 1,w['I'] = 2,w['N'] = 3,w['G'] = 4;
for(int i = 1;i <= 4;++ i)scanf("%d",&num[i]);
for(int i = 1;i <= 4;++ i)
for(int j = 1;j <= num[i];++ j)
{
cin >> a >> b;
f[i][j][1] = w[a];
f[i][j][2] = w[b];
}
scanf("%s",s + 1);
len = strlen(s) - 1;
// cout << len << endl;
for(int i = 1;i <= len;++ i)dp[i][i][w[s[i]]] = 1;
for(int i = 2;i <= len;++ i)//枚举区间长度
for(int l = 1,r = l + i - 1;l <= len - i + 1;l ++,r ++)//枚举区间的左端点和区间的右端点
for(int j = 1;j < i;++ j)//总的区间拼出来的数是由哪两个分区间组合出来的,也就是两个区间的交界处
for(int k = 1;k <= 4;++ k)//枚举WING
for(int z = 1;z <= num[k];++ z)//WING能够被组合出来的方案
if(dp[l][l + j - 1][f[k][z][1]] && dp[l + j][r][f[k][z][2]])//可行
dp[l][r][k] = 1;//标记
int fg = 0;
for(int i = 1;i <= 4;++ i)
{
if(dp[1][len][i] == true)
{
fg = 1;
cout << ans[i];
}
}
if(fg == 0)
cout << "The name is wrong!" << endl;
return 0;
}