bzoj 1055 //1055:[HAOI2008]玩具取名 循环+区间动归/记忆化搜索+区间动归

bzoj 1055 //1055:[HAOI2008]玩具取名   //在线测评地址https://www.lydsy.com/JudgeOnline/problem.php?id=1055

更多题解,详见https://blog.csdn.net/mrcrack/article/details/90228694BZOJ刷题记录

bzoj 1055

 

洛谷3.45s / 5.54MB / 1.10KB C++
方法一:循环+区间动归
//P4290 [HAOI2008]玩具取名
//在线测评地址https://www.luogu.org/problem/P4290
//弄懂样例1不容易,反复阅读,反复模拟
/*
IIII <= WW <= I
IIII <= WW <= N
输出 IN  一行字符串,该名字可能由哪些字母变形而得到。(按照WING的顺序输出)
*/
//此文https://blog.csdn.net/Clove_unique/article/details/52810486代码写得不错,思路摘抄如下:
/*
这道题和之前做过的模拟赛里的“祖先”一题很像,并且比那个简单很多。
设f[l][r][a]表示将l~r这一段能否消成a这个字符,那么假设f[i][k][b]=true,f[k+1][j][c]=true并且bc可以转换成a的话,那么f[l][r][a]也为true。这样的话就可以做到O(n4)转移了。
然后最后判断f[1][n][x]就好~(≧▽≦)/~啦!
*/
//样例通过,提交AC.2019-10-15

#include
#include
#define maxn 205
struct node{
    char x,y,z;
}ch[70];
int f[maxn][maxn][30],cnt=0,n;
char s[maxn];
void read(){
    int W,I,N,G,i;
    scanf("%d%d%d%d",&W,&I,&N,&G);
    for(i=1;i<=W;i++)scanf("%s",s),ch[++cnt].x=s[0],ch[cnt].y=s[1],ch[cnt].z='W';
    for(i=1;i<=I;i++)scanf("%s",s),ch[++cnt].x=s[0],ch[cnt].y=s[1],ch[cnt].z='I';
    for(i=1;i<=N;i++)scanf("%s",s),ch[++cnt].x=s[0],ch[cnt].y=s[1],ch[cnt].z='N';
    for(i=1;i<=G;i++)scanf("%s",s),ch[++cnt].x=s[0],ch[cnt].y=s[1],ch[cnt].z='G';
    scanf("%s",s+1),n=strlen(s+1);
}
void solve(){
    int i,len,left,right,k,flag=0;
    memset(f,0,sizeof(f));
    for(i=1;i<=n;i++)f[i][i][s[i]-'A']=1;
    for(len=2;len<=n;len++)
        for(left=1;left<=n-len+1;left++){
            right=left+len-1;
            for(k=left;k                 for(i=1;i<=cnt;i++)
                    f[left][right][ch[i].z-'A']|=(f[left][k][ch[i].x-'A']&&f[k+1][right][ch[i].y-'A']);
        }
    if(f[1][n]['W'-'A'])printf("W"),flag=1;
    if(f[1][n]['I'-'A'])printf("I"),flag=1;
    if(f[1][n]['N'-'A'])printf("N"),flag=1;
    if(f[1][n]['G'-'A'])printf("G"),flag=1;
    if(!flag)printf("The name is wrong!");
}
int main(){
    read();
    solve();
    return 0;
}

 

方法二:记忆化搜索+区间动归

bzoj 1055

1644 kb 1136 ms C++/Edit 1040 B

洛谷928ms / 1.68MB / 798B C++

//区间动归
//P4290 [HAOI2008]玩具取名
//在线测评地址https://www.luogu.org/problem/P4290
//弄懂样例1不容易,反复阅读,反复模拟
/*
IIII <= WW <= I
IIII <= WW <= N
输出 IN  一行字符串,该名字可能由哪些字母变形而得到。(按照WING的顺序输出)
*/
//此文https://blog.csdn.net/Clove_unique/article/details/52810486代码写得不错,思路摘抄如下:
/*
这道题和之前做过的模拟赛里的“祖先”一题很像,并且比那个简单很多。
设f[l][r][a]表示将l~r这一段能否消成a这个字符,那么假设f[i][k][b]=true,f[k+1][j][c]=true并且bc可以转换成a的话,那么f[l][r][a]也为true。这样的话就可以做到O(n4)转移了。
然后最后判断f[1][n][x]就好~(≧▽≦)/~啦!
*/
//样例通过,提交AC.2019-10-15
//记忆化搜索+区间动归,此文http://hzwer.com/5333.html代码写得不错
//样例通过,提交AC.2019-10-16
#include
#include
int t[5];
char a[4][20][5],p[]="WING",ch[205];
int q[100],f[205][205][5];
int dp(int left,int right,int k){
    int i,j;
    int &ret=f[left][right][k];//&引用,ret就直接代表f[left][right][k],原因是写ret比写f[left][right][k]容易
    if(left==right)return ch[left]==p[k];
    if(ret!=-1)return ret;//记忆化搜索
    for(i=0;i         for(j=left;j             if(dp(left,j,q[a[k][i][0]])&&dp(j+1,right,q[a[k][i][1]]))
                return ret=1;
    return ret=0;
}
int main(){
    int i,j,n,flag=0;
    for(i=0;i<4;i++)scanf("%d",&t[i]);
    for(i=0;i<4;i++)
        for(j=0;j             scanf("%s",a[i][j]);
    scanf("%s",ch+1),n=strlen(ch+1);
    memset(f,-1,sizeof(f)),q['W']=0,q['I']=1,q['N']=2,q['G']=3;
    for(i=0;i<4;i++)
        if(dp(1,n,i))printf("%c",p[i]),flag=1;
    if(!flag)printf("The name is wrong!");
    return 0;
}

 

你可能感兴趣的:(跟着大佬学算法)