NKOJ-3748 玩具取名

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!");
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值
>