1. 程式人生 > >【區間DP】BZOJ1055(HAOI2008)[玩具取名]題解

【區間DP】BZOJ1055(HAOI2008)[玩具取名]題解

題目概述

JZ選擇WING四個字母中的任意一個字母作為JZ的基本網名。然後JZ會根據自己的喜好,將名字中任意一個字母用“WING”中任意兩個字母代替(會給出字母能變成哪些雙字母),使得自己的網名能夠擴充得很長。現在,他想請你猜猜某一個很長的網名,最初可能是由哪幾個字母變形過來的。

解題報告

DP功底不行了啊QAQ,這應該不算很難的區間DP吧……

定義 f[i][j][k] 表示 [i,j] 是否能變成 k ,然後只需要列舉斷開的地方,轉移一下就行了。

示例程式

#include<cstdio>
#include<cctype>
using namespace std; const int maxn=200,maxe=4*16; int n,ID[256],m[4],E,lnk[16],nxt[maxe+5],son[maxe+5]; char s[4];bool f[maxn+5][maxn+5][4]; inline char getupr() {char ch=getchar();while (!isupper(ch)) ch=getchar();return ch;} #define Add(x,y) son[++E]=y,nxt[E]=lnk[x],lnk[x]=E #define val(x,y) ((x)<<2|(y))
int main(){ freopen("program.in","r",stdin); freopen("program.out","w",stdout); ID[s[0]='W']=0;ID[s[1]='I']=1;ID[s[2]='N']=2;ID[s[3]='G']=3; for (int i=0;i<4;i++) scanf("%d",&m[i]); for (int t=0;t<4;t++) for (int now;m[t];m[t]--) now=ID[getupr()],now=now<<2
|ID[getupr()],Add(now,t); char lst=0,ch=getupr(); for (n=1;isupper(ch);lst=ch,ch=getchar(),n++){ f[n][n][ID[ch]]=true;if (!lst) continue; for (int j=lnk[val(ID[lst],ID[ch])];j;j=nxt[j]) f[n-1][n][son[j]]=true; } for (int len=(n--,3);len<=n;len++) for (int i=1,j=len;j<=n;i++,j++) for (int k=i;k<j;k++) for (int x=0;x<4;x++) for (int y=0;y<4;y++) if (f[i][k][x]&&f[k+1][j][y]) for (int e=lnk[val(x,y)];e;e=nxt[e]) f[i][j][son[e]]=true; bool fl=false;for (int i=0;i<4;i++) if (f[1][n][i]) fl=true,putchar(s[i]); return puts(fl?"":"The name is wrong!"),0; }