【poj 2411】Mondriaan's Dream 状压dp(轮廓线性dp)

来源:互联网 发布:淘丫丫网络是真是假 编辑:程序博客网 时间:2024/06/06 04:01

好吧,终于可以来填这一道题的坑了,说来我和这一道题也是有些渊源了吧,因为是一道非常经典的轮廓线性dp所以后人的改进算法也是非常之多,导致出现了一大批位运算技巧满天飞的看不懂,每次又静不下来写,所以耽搁了很久了。

参考代码:http://www.cnblogs.com/keam37/p/3834490.html?utm_source=tuicool


其实还是比较喜欢这种用dfs写的,代码短,时间快,没有重复的枚举不必要的状态,这里定义f[i][j]表示在第i行,当前状态是j的方法数,然后每一行的f就可以从前一行的递推出来,对于每一行的每一列,都会有三种可能的情况

1.竖着放    要求:上一行一定没有覆盖

2.不放        要求:上一行一定是覆盖了的(不然的话,上一列将永远也填不上)

3.横着放    要求:上一行的第j列和第j+1列一定都是覆盖了的,原因同上

要是还不懂就看代码吧


注意:初始化第0行全部放慢的状态的方案数为1,因为在第1行骨牌不能是竖着放的,具体看代码

#include<cstdio>#include<cstring>#include<iostream>#define ll long longusing namespace std;int n,m,x; ll f[14][1<<12+1];void dfs(int k,int last,int now){if(k==m){f[x][now]+=f[x-1][last];//由之前的状态来更新这一列的状态 return;}if(k>m)return;//因为会加2,所以可能会大于m dfs(k+1,last<<1,now<<1|1);//竖着放 dfs(k+1,last<<1|1,now<<1);//不放 dfs(k+2,last<<2|3,now<<2|3);//横着放 }int main(){while(scanf("%d%d",&n,&m)!=EOF&&n+m){if(n*m%2){//如果有奇数就不可能会有答案 printf("0\n");continue;}memset(f,0,sizeof(f));f[0][(1<<m)-1]=1;//只有假设的第0行全部放满才能放第一行,因为第一行并不能竖着放就和第0行放满是一样的结果 for(x=1;x<=n;x++)//依次处理每一行 dfs(0,0,0);printf("%lld\n",f[n][(1<<m)-1]);}return 0;}




0 0