1 条题解

  • 0
    @ 2025-8-24 21:58:37

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar 狄凡人
    **

    搬运于2025-08-24 21:58:37,当前版本为作者最后更新于2019-03-12 21:52:49,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    第一篇题解,望各位大佬支持,谢谢

    首先面对这道题,我的第一个困难是怎么读入数据QWQ 首先说明一下变量的含义:

    bool dp[maxn][maxn][5],can[5][5][5];//f[i j k]表示区间 i,j由 k转化.can[ i j k]表示i可以由j、k转化
    int q[5],len=0;//q是每个字母转化成几对字母
    char s[maxn];//原名字
    int change(char i)//把字母转化成数字
    {  
        if(i=='W') return 1; 
        if(i=='I') return 2;
        if(i=='N') return 3; 
        if(i=='G') return 4; 
    } 
    

    再同学们的帮助下,历经了两天晚自习,我成功的读入了数据

    int main() {
        for(int i=1;i<=4;i++) scanf("%d",&q[i]);//有几对字母可以转化
        for(int i=1;i<=4;i++) 
    	{
            for(int j=1;j<=q[i];j++) 
    		{
                scanf("%s",&c);//直接读到空格或者回车结束
                can[i][change(c[0])][change(c[1])]=true;//第一个字母可以由二、三个转化过来
            }
        }
        
        scanf("%s",s+1); //从s+1开始读入
        for(len=1;s[len];len++);//只要没结束,就继续读
        len--;//会多一个,所以--
    

    接下来才是解读: 这是一道标准的区间DP,原因很简单,要把它分区间求解,并且之前处理的结果会对之后产生影响,具体问题见注释

    for(int i=1;i<=len;i++) dp[i][i][change(s[i])]=true;//从i到i可以由他自己转化
        
        for(int led=1;led<len;led++)//列举长度的可能性
            for(int l=1;l<=len-led;l++)//列举左界的可能性 
    		{
                int r=l+led;//右界可以算出来
                for(int k=l;k<r;k++) //在左右之间列举可能的断点 
                    for(int z=1;z<=4;z++)  //列举l到r(需要的区间)可能转化成的字母
                        for(int z1=1;z1<=4;z1++) // 列举l到k(借助的左区间)
                            for(int z2=1;z2<=4;z2++)  //列举k + 1到r(借助的右区间)
                                if(can[z][z1][z2] && dp[l][k][z1] && dp[k+1][r][z2]) 
    //如果z1、z2可以转化成z 并且 l到k可以变成z1 并且k+1到r可以变成z2
                                    dp[l][r][z]=true;
    //那么l到r就可以转化成z
            }
    

    整个dp过程大概可以理解为 | 121...233 | + | 232...343 | | :----------: | :----------: | :----------: | | 可以由1转化 | | 可以由2转化 | 并且1 2 可以转化成2 那么 | 121....233232...343 | | :----------: | | 可以由2转化 |

    输出也很简单,只需要查询1~len可不可以被四个字母取代

    bool f=false;
        if(dp[1][len][1]) {f=true;printf("W");}
        if(dp[1][len][2]) {f=true;printf("I");}
        if(dp[1][len][3]) {f=true;printf("N");}
        if(dp[1][len][4]) {f=true;printf("G");}
        if(!f) printf("The name is wrong!");
    

    那么... 附AC代码,如有意见欢迎私信本蒟蒻

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    const int maxn =210;
    bool dp[maxn][maxn][5],can[5][5][5];//f[i j k]表示区间 i,j由 k转化.
    int q[5],len=0;
    char s[maxn];
    int change(char i){  
        if(i=='W') return 1; 
        if(i=='I') return 2;
        if(i=='N') return 3; 
        if(i=='G') return 4; 
    } 
    int main() {
        for(int i=1;i<=4;i++) scanf("%d",&q[i]);
        for(int i=1;i<=4;i++) 
    	{
            for(int j=1;j<=q[i];j++) 
    		{
                scanf("%s",&c);
                can[i][change(c[0])][change(c[1])]=true;
            }
        }
        
        scanf("%s",s+1); 
        for(len=1;s[len];len++);
        len--;
        for(int i=1;i<=len;i++) dp[i][i][change(s[i])]=true;
        
        for(int led=1;led<len;led++)//列举长度 
            for(int l=1;l<=len-led;l++)//列举左界 
    		{
                int r=l+led;
                for(int k=l;k<r;k++) //列举断点 
                    for(int z=1;z<=4;z++)  //列举l到r
                        for(int z1=1;z1<=4;z1++) // 列举l到k
                            for(int z2=1;z2<=4;z2++)  //列举k + 1到r
                                if(can[z][z1][z2] && dp[l][k][z1] && dp[k+1][r][z2]) 
                                    dp[l][r][z]=true;
            }
        bool f=false;
        if(dp[1][len][1]) {f=true;printf("W");}
        if(dp[1][len][2]) {f=true;printf("I");}
        if(dp[1][len][3]) {f=true;printf("N");}
        if(dp[1][len][4]) {f=true;printf("G");}
        if(!f) printf("The name is wrong!");
        return 0;
    }
    
    • 1

    信息

    ID
    3259
    时间
    1000ms
    内存
    125MiB
    难度
    4
    标签
    递交数
    0
    已通过
    0
    上传者