HDU 4804 轮廓线DP

来源:互联网 发布:mysql数据库的触发器 编辑:程序博客网 时间:2024/06/08 10:47

题意

给一个网格,里面能放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<<endlusing 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);    }}
原创粉丝点击