P3748玩具取名
时间限制 : - MS 空间限制 : 65536 KB
评测说明 : 时限1000ms
问题描述
某人有一套玩具,并想法给玩具命名。首先他选择WING四个字母中的任意一个字母作为玩具的基本名字。然后他会根据自己的喜好,将名字中任意一个字母用“WING”中任意两个字母代替,使得自己的名字能够扩充得很长。现在,他想请你猜猜某一个很长的名字,最初可能是由哪几个字母变形过来的。
输入格式
第一行四个整数W、I、N、G。表示每一个字母能由几种两个字母所替代。
接下来W行,每行两个字母,表示W可以用这两个字母替代。
接下来I行,每行两个字母,表示I可以用这两个字母替代。
接下来N行,每行两个字母,表示N可以用这两个字母替代。
接下来G行,每行两个字母,表示G可以用这两个字母替代。
最后一行一个长度不超过Len的字符串。表示这个玩具的名字。
输出格式
一行字符串,该名字可能由哪些字母变形而得到。(按照WING的顺序输出)
如果给的名字不能由任何一个字母变形而得到则输出“The name is wrong!”
样例输入
1 1 1 1
II
WW
WW
IG
IIII
样例输出
IN
附加样例(检验用)
Input
1 2 1 1
IG
IW
GI
II
GW
IIGIG
Output
IN
提示
W可以变成II所以IIII可以缩成WW IN均能变成WW所以WW又可以缩成I或者N 所以最终答案应该按照“WING”的顺序输出IN
[数据范围]
100%数据满足Len<=200,W、I、N、G<=16
首先
这个人真的非常作
没事给玩具取名字,简直无聊
然后
就是这道题的思路了
这道题目的重点在于最后的结果是一个字母
一个字母是由两个字母缩起来得到的
所以说这一整段一定是可以缩成两个字母
那这两个字母又是通过三个或四个字母得到的(有一个字母是可以保留的)
比如样例当中
IIII=>WW=>I或N (四个缩成一个)
IIG=>IG=>G (三个缩成一个)
所以这道题目的思路就出来了
一整段分开来,不停地尝试把一段分成很多个两个字母和一个字母的组合
然后一部分一部分缩成一个字母
然后再继续缩
所以这又是一道分治思想的题目
通过分治来简化问题
然后
为了降低时间复杂度,用记忆化搜索
这道题目的状态很好定
bool res[l,r][k]=>对于[l,r]这段区间的字母,能否缩成字母k
有一点要注意就是
一个字母可以由多组两个字母得到(样例很明显)
所以我们的k是要开数组的
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int ch[5],wing[234];
bool rep[5][5][5],res[234][234][5];
char Out[5]={' ','W','I','N','G'},use[234];
int REP(char c)//替换字符
{
if(c=='W')return 1;
if(c=='I')return 2;
if(c=='N')return 3;
if(c=='G')return 4;
}
void Rep(int s,int e,int o,int t)//缩字符
{
for(int i=1;i<=4;i++)
if(rep[o][t][i])res[s][e][i]=1;
}
void DP(int s,int e)
{
if(res[s][e][0])return;
res[s][e][0]=1;
if(s==e){res[s][e][wing[s]]=1;return;}//分治最终的情况处理
int a,b;bool o,t;
for(int dep=s;dep<e;dep++)
{
DP(s,dep);DP(dep+1,e);
for(a=1,o=res[s][dep][a];a<=4;a++,o=res[s][dep][a])
for(b=1,t=res[dep+1][e][b];b<=4;b++,t=res[dep+1][e][b])//根据思路意会吧
if(o&&t)Rep(s,e,a,b);
}
}
int main()
{
char a,b;int o,t,n;bool flag=0;
scanf("%d%d%d%d\n",&ch[1],&ch[2],&ch[3],&ch[4]);
for(int i=1;i<=4;i++)
for(int ct=1;ct<=ch[i];ct++)
{
scanf("%c%c\n",&a,&b);
o=REP(a);t=REP(b);
rep[o][t][i]=1;//两个字符缩成一个字符的判定
}
scanf("%s",&use);n=strlen(use);
for(int i=0;i<n;i++)wing[i]=REP(use[i]);//把原字符串转成int->方便后续判定
DP(0,n-1);
for(int i=1;i<=4;i++)if(res[0][n-1][i])flag=1,printf("%c",Out[i]);
if(!flag)printf("The name is wrong!");
}