BZOJ 1055

1055: [HAOI2008]玩具取名

Time Limit: 10 Sec   Memory Limit: 162 MB
Submit: 887   Solved: 518
[ Submit][ Status]

Description

某人有一套玩具,并想法给玩具命名。首先他选择WING四个字母中的任意一个字母作为玩具的基本名字。然后他会根据自己的喜好,将名字中任意一个字母用“WING”中任意两个字母代替,使得自己的名字能够扩充得很长。现在,他想请你猜猜某一个很长的名字,最初可能是由哪几个字母变形过来的。

Input

第一行四个整数W、I、N、G。表示每一个字母能由几种两个字母所替代。接下来W行,每行两个字母,表示W可以用这两个字母替代。接下来I行,每行两个字母,表示I可以用这两个字母替代。接下来N行,每行两个字母,表示N可以用这两个字母替代。接下来G行,每行两个字母,表示G可以用这两个字母替代。最后一行一个长度不超过Len的字符串。表示这个玩具的名字。

Output

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

Sample Input

1 1 1 1
II
WW
WW
IG
IIII

Sample Output

IN

HINT

W可以变成II所以IIII可以缩成WW IN均能变成WW所以WW又可以缩成I或者N 所以最终答案应该按照“WING”的顺序输出IN 

[数据范围]

100%数据满足Len<=200,W、I、N、G<=16





【题解】

 

1055:

题目大意是 给你一个变形后的串,问你它可能是由哪几个字符变化得来的。

一、
我想的,最后的答案只有四个,所以或许可以一个一个试出来。

那么得到目标的字符串,需要 len-1 次变化。
	
分治思想:

I   I    I    I
 \ /      \ /
 W     W
    \   /
    I(N)

dfs(n) =(16)* len(n) * { dfs(x) + dfs(n-x) };
 
很明显这是铁定TLE 的,于是我苦思冥想,突然想到加上记忆化怎么样。
如果加上记忆化 状态数 最多是 len*len*4 中,加上每种状态 的 16 次枚举哪一个变化,得 O( n*n*64 )的时间复杂度,就可以过了。
 
#include<cstdio>
#include<cstring>
#include<cstdlib>

int a[26],n[4],f[4][210][210],len;
char c[4][18][3],s[210];

void dfs(int x,int L,int R)
{
	if(f[x][L][R]!=(-1))return;
	if(L==R)
	{
		f[x][L][R]=(x==a[s[L]-'A']);
		return;
	}
	for(int i=1;i<=n[x];i++)
	{
		for(int j=L;j<R;j++)
		{
			if(j==L && s[L]!=c[x][i][1])continue;
			if((j+1)==R && s[R]!=c[x][i][2])continue;
			
			if(!f[a[c[x][i][1]-'A']][L][j])continue;
			if(!f[a[c[x][i][2]-'A']][j+1][R])continue;
			
			if(f[a[c[x][i][1]-'A']][L][j]==(-1))dfs(a[c[x][i][1]-'A'],L,j);
			if(!f[a[c[x][i][1]-'A']][L][j])continue;
			
			if(f[a[c[x][i][2]-'A']][j+1][R]==(-1))dfs(a[c[x][i][2]-'A'],j+1,R);
			
			if(f[a[c[x][i][2]-'A']][j+1][R]==1 && f[a[c[x][i][1]-'A']][L][j]==1)
			{
				f[x][L][R]=1; break;
			}
		}
		if(f[x][L][R]!=(-1))break;
	}
	if(f[x][L][R]==(-1))f[x][L][R]=0;
}
int main()
{
	scanf("%d%d%d%d",&n[0],&n[1],&n[2],&n[3]);
	memset(f,-1,sizeof(f));
	a['W'-'A']=0; a['I'-'A']=1;
	a['N'-'A']=2; a['G'-'A']=3;
	for(int i=1;i<=n[0];i++)scanf("%s",c[0][i]+1);
	for(int i=1;i<=n[1];i++)scanf("%s",c[1][i]+1);
	for(int i=1;i<=n[2];i++)scanf("%s",c[2][i]+1);
	for(int i=1;i<=n[3];i++)scanf("%s",c[3][i]+1);
	scanf("%s",s+1);
	len=strlen(s+1);
	int key=0;
	for(int i=0;i<4;i++)
	{
		dfs(i,1,len);
		if(f[i][1][len]==1)
		{
			switch (i)
			{
				case 0:printf("W"); break;
				case 1:printf("I"); break;
				case 2:printf("N"); break;
				case 3:printf("G"); break;
			}
			key=1;
		}
	}
	if(!key)printf("The name is wrong!");
	printf("\n");  return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值