zoukankan      html  css  js  c++  java
  • 洛谷2593 [ZJOI2006]超级麻将——可行性dp

    题目:https://www.luogu.org/problemnew/show/P2593

    发现三个连续牌的影响范围只有3、相同牌的影响范围只有1之后就可以dp了。

    O(100^7)T飞。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int N=105;
    int T,a[N];
    bool dp[2][N][N][N],flag;
    int rdn()
    {
        int ret=0;char ch=getchar();
        while(ch>'9'||ch<'0')ch=getchar();
        while(ch>='0'&&ch<='9')(ret*=10)+=ch-'0',ch=getchar();
        return ret;
    }
    int main()
    {
        T=rdn();
        while(T--)
        {
            for(int i=1;i<=100;i++)a[i]=rdn();
            for(int t=1;t<=100;t++)if(a[t]>=2)
            {
                a[t]-=2;
                memset(dp[1],0,sizeof dp[1]);
                dp[1][a[1]][a[2]][a[3]]=1;
                for(int i=3;i<=102;i++)
                {
                    flag=0;int d=(i&1);
                    memset(dp[!d],0,sizeof dp[!d]);
                    for(int x=0;x<=a[i-2];x++)
                        for(int y=0;y<=a[i-1];y++)
                            for(int z=0;z<=a[i];z++)
                                if(dp[d][x][y][z])
                                {
                                    int lm=min(x,min(y,z));
                                    for(int k=0;k<=lm;k++)
                                    {
                                        dp[!d][y-k][z-k][a[i+1]]|=(((x-k)%3==0)|((x-k)%4==0));
                                        if(dp[!d][y-k][z-k][a[i+1]])flag=1;
                                    }
                                }
                    if(!flag)break;
                }
                a[t]+=2;
                if(flag)break;
            }
            if(flag)printf("Yes
    ");else printf("No
    ");
        }
        return 0;
    }
    View Code

    如果不枚举对子在哪而是带一个0/1在状态里的话可以少乘一个100。

    如果不无脑记录前两个位置而是仅记录前一个位置、用三连的时候调用一下上一层的话可以少乘一个100。

      这是因为最后要的只有dp[ ][0][0][0],所以保存很多“之前位置还不是0”的状态比较浪费。

    不枚举用了几个三连,而是每层用“从开头降到这里”更新一下本层,就能把”在本层用较少的三连“包括在“用单个位置从之前层降下来”。这样可以少乘一个100。

      这里调了好久!必须是“从开头降到这里”,而不能是“从这里降到0”;因为从“这里”降的话需要判断本层是否可行,但这个可行是包括 i-2 的状态的,自己仅记录前一个位置的dp不能存下。如果是”从开头降到这里“的话,本层目前一定是可行的。

    O(100^4)卡过。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int N=110;
    int T,a[N];
    bool dp[N][N][N][2];
    int rdn()
    {
        int ret=0;char ch=getchar();
        while(ch>'9'||ch<'0')ch=getchar();
        while(ch>='0'&&ch<='9')(ret*=10)+=ch-'0',ch=getchar();
        return ret;
    }
    int main()
    {
        T=rdn();
        while(T--)
        {
            memset(dp,0,sizeof dp);
            for(int i=1;i<=100;i++)a[i]=rdn();
            dp[1][0][a[1]][0]=1;
            for(int i=1;i<=100;i++)
            {
                for(int x=a[i-1];x>=0;x--)
                    for(int y=a[i];y>=0;y--)
                    {
                        dp[i][x][y][1]|=dp[i][x][y+2][0];
                        
                        dp[i][x][y][0]|=dp[i][x][y+3][0];
                        dp[i][x][y][1]|=dp[i][x][y+3][1];
                        
                        dp[i][x][y][0]|=dp[i][x][y+4][0];
                        dp[i][x][y][1]|=dp[i][x][y+4][1];
                        //在a[i]变成各种y之后尝试一下从开头降到这里(先降后单个弄) 
                        dp[i][x][y][0]|=dp[i-1][a[i]-y][x+a[i]-y][0];
                        dp[i][x][y][1]|=dp[i-1][a[i]-y][x+a[i]-y][1];
                        
                        //在a[i]变成各种y之后尝试一下取完 
    //                    dp[i][x][0][0]|=dp[i-1][y][x+y][0];//不能&dp[i][x+y][y],因含义是i-2==0 
    //                    dp[i][x][0][1]|=dp[i-1][y][x+y][1];//不&的话会出错:0 3 0(原为2) 
                        //因为从当前降到0的话,需要判断当前是否可行
                        //可是降还需要i-2的也许有值,与表示“当前可行”的dp冲突,即无法表示当前是否可行 
                        //而“从开头”降的话,一定可行 
                        
                        //x之所以不是0,是因为这里用到dp[i-1][y][][] 
                        if(!x)
                        {
                            dp[i+1][y][a[i+1]][0]|=dp[i][0][y][0];
                            dp[i+1][y][a[i+1]][1]|=dp[i][0][y][1];
                        }
    //                    if(i<=3)
    //                    {
    //                        if(dp[i][x][y][0])printf("dp[%d][%d][%d][%d]=%d
    ",i,x,y,0,dp[i][x][y][0]);
    //                        if(dp[i][x][y][1])printf("dp[%d][%d][%d][%d]=%d
    ",i,x,y,1,dp[i][x][y][1]);
    //                    }
                    }
            }
            puts(dp[100][0][0][1]?"Yes":"No");
        }
        return 0;
    }
  • 相关阅读:
    正则表达式
    浅谈xss攻击
    四舍五入[银行家算法]
    POJ-2442-Sequence(二叉堆)
    Spring MVC 启动报错
    WebMagic 抓取图片并保存至本地
    spring 定时任务
    jquery validate 自定义校验方法
    位图
    二叉树(线索化)
  • 原文地址:https://www.cnblogs.com/Narh/p/9370833.html
Copyright © 2011-2022 走看看