HDU 4804 轮廓线DP

题意

给一个网格,里面能放1*1和1*2的方格,标记为0的地方不能放方格。问有多少种摆放方式。

题解

刘汝佳的训练指南上有这个的简化版本,在哪个版本中不存在1*1的方格和不能放置的方格。但是实际上,这种放方格的问题,无论有多少要求,轮廓线DP都能很好的解决。
深入理解轮廓线DP是解决这道题的前提条件,普通的状压DP是针对行来考虑的,而轮廓线DP是针对格子来考虑的。对行进行的考虑,只能依靠枚举两行的状态来判断是否可以进行状态转移,对于一些比较复杂的要求,根本无法实现。但是轮廓线DP就不一样了,由于操作单元是格,因此可以实现一些比较复杂的DP问题。
对于这道题,我们在进行DP的时候首先要考虑格子是否能放置方格,对于不能放置方格的情况,我们直接假设这个地方已经放置了一个1*1的方格。对于可以放置方格的情况,我们考虑四种情况,1*1,1*2横放,1*2竖放,不放。我们可以将更新操作提取成一个函数,这样的话对于取模操作可以比较方便地去实现。

代码

#include<bits/stdc++.h>
#define UP(i,l,h) for(int i=l;i<h;i++)
#define W(t) while(t)
#define MEM(a,b) memset(a,b,sizeof(a))
#define LL long long
#define INF 0x3f3f3f3f
#define eps 1e-10
#define MAXN 3000010
#define MOD 1000000007
#define COUT(x) cout<<x<<endl
using namespace std;
char mp[110][12];
LL dp[2][1100][25];
int pre,now;
int n,m,c,d;
void update(int a,int k,int p,int add){
    if(k&(1<<m)){
      dp[now][k^(1<<m)][p+add]=(dp[now][k^(1<<m)][p+add]+dp[pre][a][p])%MOD;
    }
}
int main() {
    W(~scanf("%d%d%d%d",&n,&m,&c,&d)) {
        UP(i,0,n) {
            scanf("%s",mp[i]);
        }
        MEM(dp,0);
        pre=0,now=1;
        dp[now][(1<<m)-1][0]=1;
        UP(i,0,n){
            UP(j,0,m){
                swap(now,pre);
                MEM(dp[now],0);
                UP(k,0,(1<<m)){
                    UP(p,0,d+1){
                        if(mp[i][j]-'0'!=0){
                            update(k,k<<1,p,0); //no
                            update(k,(k<<1)|1,p,1); //one
                            if(j&&!(k&1)) update(k,(k<<1)|3,p,0); //left
                            if(!(k&(1<<(m-1)))) update(k,(k<<1)|(1<<m)|1,p,0);//up
                        }else{
                            update(k,(k<<1)|1,p,0);
                        }
                    }
                }
            }
        }
        LL ans=0;
        UP(p,c,d+1){
            ans=(ans+dp[now][(1<<m)-1][p])%MOD;
//            COUT(dp[now][(1<<m)-1][p]);
        }
        printf("%I64d\n",ans);
    }
}

猜你喜欢

转载自blog.csdn.net/zhenlingcn/article/details/78187415
今日推荐