HDU 4363 Draw and paint (DP)

转载请注明出处,谢谢 http://blog.csdn.net/ACM_cxlove?viewmode=contents           by---cxlove

题目:一张纸,轮流在纸上选择一块画横线和竖线,分为两块,将一块染色,问最终纸上颜色情况有多少种。

http://acm.hdu.edu.cn/listproblem.php?vol=1

感谢zz_1215的指导

7维DP,其实可以优化到6维,横切和竖切的关系在于,旋转之后再切。

dp[x][y][l][r][u][d][k]表示长宽为x,y,上,下,左,右的颜色分别为l,r,u,d时的染色种数,而且与边界的颜色四周不能相同。我们将4种颜色标为1,2,3,4再取空白色为0。当K为0的时候,表示接下来横切,K为1的时候表示接下来竖切。

意思是说,如果有一1*1的要染色,上面已经是红色了,那么这里就不能染红色,因为这在2*1里面已经统计过了。

首先对于不能再切的情况,就是枚举4种颜色了。

否则枚举切的位置以及颜色。当切了一刀之后,分为两块,两块的颜色可能一样,这样就重复了,这里需要减掉。

而且还要把整块的颜色都相同的加上。

记忆化搜索。。。总觉得在写法在可以优化,尝试了好多次,总是有细节处理不好,哎

#include<iostream>
#include<cstdio>
#include<cstring>
#define inf 1<<29
#define LL long long
#define MOD 1000000007
using namespace std;
int dp[41][41][5][5][5][5][2];
int get_dp(int x,int y,int l,int r,int u,int d,int k){
    if(dp[x][y][l][r][u][d][k]!=-1)
        return dp[x][y][l][r][u][d][k];
    dp[x][y][l][r][u][d][k]=0;
    //处理不能再切的情况
    if((x==1&&k==0)||(y==1&&k)){
        for(int i=1;i<5;i++)
           if(i!=l&&i!=r&&i!=u&&i!=d)
               dp[x][y][l][r][u][d][k]++;
        return dp[x][y][l][r][u][d][k];
    }
    dp[x][y][l][r][u][d][k]=0;
    if(!k){
        for(int i=1;i<x;i++)
            for(int j=1;j<5;j++){
                if(j!=u&&j!=l&&j!=r)
                    dp[x][y][l][r][u][d][k]=(dp[x][y][l][r][u][d][k]+get_dp(x-i,y,l,r,j,d,1))%MOD;
                if(j!=d&&j!=l&&j!=r)
                    dp[x][y][l][r][u][d][k]=(dp[x][y][l][r][u][d][k]+get_dp(i,y,l,r,u,j,1))%MOD;
            }
        //有t种可能重复,而切的位置有x-1种
        int t=0;
        for(int i=1;i<=4;i++)
            if(i!=u&&i!=l&&i!=r)
                for(int j=1;j<=4;j++)
                    if(j!=d&&j!=l&&j!=r&&j!=i)
                       t++;
        dp[x][y][l][r][u][d][k]=(dp[x][y][l][r][u][d][k]+MOD-t*(x-1))%MOD;
        //整 个区域都同色
        for(int i=1;i<5;i++)
            if(i!=l&&i!=r&&i!=u&&i!=d)
                dp[x][y][l][r][u][d][k]++;
    }
    else{
        for(int i=1;i<y;i++)
            for(int j=1;j<5;j++){
                if(j!=l&&j!=d&&j!=u)
                    dp[x][y][l][r][u][d][k]=(dp[x][y][l][r][u][d][k]+get_dp(x,y-i,j,r,u,d,0))%MOD;
                if(j!=r&&j!=d&&j!=u)
                    dp[x][y][l][r][u][d][k]=(dp[x][y][l][r][u][d][k]+get_dp(x,i,l,j,u,d,0))%MOD;
            }
        int t=0;
        for(int i=1;i<=4;i++)
            if(i!=l&&i!=u&&i!=d)
                for(int j=1;j<=4;j++)
                   if(j!=r&&j!=u&&j!=d&&j!=i)
                      t++;
        dp[x][y][l][r][u][d][k]=(dp[x][y][l][r][u][d][k]+MOD-t*(y-1))%MOD;
        for(int i=1;i<5;i++)
            if(i!=l&&i!=r&&i!=u&&i!=d)
                dp[x][y][l][r][u][d][k]++;
    }
    return dp[x][y][l][r][u][d][k]%MOD;
}
int main(){
    memset(dp,-1,sizeof(dp));
    int t;
    scanf("%d",&t);
    while(t--){
        int x,y;
        scanf("%d%d",&x,&y);
        printf("%d\n",get_dp(x,y,0,0,0,0,0));
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值