T解 POJ-2411 Mondriaan's Dream [轮廓线DP] || [状压DP]
来源:互联网 发布:天津基础教育网络平台 编辑:程序博客网 时间:2024/04/30 12:10
大家都很强,可与之共勉。
今天考试遇到了一道神题,用俄罗斯方块铺地板地板上还有柱子(数据范围十分感人)。std是要用轮廓线DP。在此之前没有听说过轮廓线DP,故开始学习。当然看到这一道入门级的题。最初做的时候是用的状压DP(其实是正解+打表)。
轮廓线DP显然是按点DP,分析该点之前的状态,若合法,则从该状态更新。初始化即为f[0][(1 << m) - 1] = 1(希望大家自己想清楚为什么这么做,我也想了好一会儿)。
是这样的初始值为f[0][<11111>]=1,其他f[0][其他值]=0(其实这个可以不弄,因为首行不能上放,所以首行轮廓线状态数不可能会引用到f[0][01111]这样的初值。)。
首先分析状态,我们发现这一一个点的状态只与上一个点有关,所以用滚动数组强行优化空间(否则DP数组就需要n * m * ( 1 << m - 1 )的空间)。
默认枚举点的顺序是从上到下,从左到右, 二进制bit(1)代表放了,bit(0)表示没有放。
如此分析,有几种情况呢?
答案是三种:1.不放 2.横着放(占着当前点与左边的点, 为什么是左边的呢, 因为状态要从前面的点转移啊!)3.竖着放(占着这个与正上方那一个)
设k为第(i, j)点的待判定状态。
接下来就是判合法,我们可以确定,对于1如果这个点的正上方没放,那么就不合法。如何判断呢?
if(k & (1 << (m - 1))) f[cur][(k << 1)^ (1 << m)] += f[cur ^ 1][k];//等价于if((k << 1)& (1 << m)) f[cur][(k << 1)^ (1 << m)] += f[cur ^ 1][k];
不难理解,画一个图模拟就好。
对于2,还有一个限制条件,就是这一个点不能处于第一列(用0表示),此外要求它的左边没有,正上方就一定要有(这也是每一个状态所要求的)。最后把最正上方的点改为1,把正上方左边的点标为0(^(1 << m))(因为只能转移m位的状态,留着也用不上)。
if(j && !(k & 1)) if(((k << 1) ^ 3) & (1 << m)) f[cur][((k << 1) ^ 3) ^ (1 << m)] += f[cur ^ 1][k];
对于3,要求正上方没有,同理判断,这个点不能处于第一行(同样用0表示)。
if(i && !(k & (1 << (m - 1)))) if( ((k << 1) ^ (1 << m) ^ 1) & (1 << m) ) f[cur][((k << 1) ^ (1 << m) ^ 1) ^ (1 << m)] += f[cur ^ 1][k];
那么显而易见,完整代码如下(判断和非法有点繁琐,可以优化)
#include "cstdio"#include "cstring"#define swap(a, b) {a ^= b, b ^= a, a ^= b;}typedef long long LL;int n, m, cur, pre;LL f[2][1 << 11], a[50][101];int main() <% memset(a, 0, sizeof(a)); while(scanf("%d%d", &n, &m) == 2 && n && m) { if(m > n) swap(m, n); if(n & 1 && m & 1) { puts("0"); continue; } if(a[m][n]) { printf("%lld\n", a[m][n]); continue; } memset(f, 0, sizeof(f)); cur = 0; f[0][(1 << m) - 1] = 1; for (int i = 0; i < n; ++i) for(int j = 0; j < m; ++j) { cur ^= 1; memset(f[cur], 0, sizeof(f[cur])); for(int k = 0; k < (1 << m); ++k) { if((k << 1) & (1 << m)) f[cur][(k << 1) ^ (1 << m)] += f[cur ^ 1][k]; if(i && !(k & (1 << (m - 1)))) if( ((k << 1) ^ (1 << m) ^ 1) & (1 << m) ) f[cur][((k << 1) ^ (1 << m) ^ 1) ^ (1 << m)] += f[cur ^ 1][k]; if(j && !(k & 1)) if(((k << 1) ^ 3) & (1 << m)) f[cur][((k << 1) ^ 3) ^ (1 << m)] += f[cur ^ 1][k]; } } printf("%lld\n", (a[m][n] = f[cur][(1 << m) - 1])); }%>
当然给大家我当初Naive的状压DP版本(单组数据的)
#include <cstdio>#include <iostream>#include <algorithm>using namespace std;long long f[12][(1 << 11) +1];int r,c;inline bool init(int i) {// 1 * 2; int k = 0; while( k < c ) { if(i & (1<<k)) { if(k == c - 1) return false; if( i & (1 << (k+1)) ) k += 2; else return false; } else ++k; } return true;}inline int judge( int now, int pre ) { int k = 0; while(k < c) { if(now & ( 1 << k ) ) if( pre & ( 1 << k ) ) if( k == c - 1 || !( now & ( 1 << ( k + 1 ) ) ) || !( pre & ( 1 << ( k + 1 ) ) ) ) return false; else k += 2; else ++k; else if( pre & ( 1 << k ) ) ++k; else return 0; } return 1;}long long dp( int r, int c ) { for ( int i = 0; i <= (1 << c) - 1; ++i ) if(init(i)) f[1][i] = 1; for ( int l = 2; l <= r; ++l ) for ( int now = 0; now <= (1 << c) - 1; ++now ) for ( int pre = 0; pre <= (1 << c) - 1; ++pre ) f[l][now] += f[l-1][pre] * judge(now, pre); return f[r][(1<<c) - 1];}int main() { freopen("domino.in","r",stdin);freopen("domino.out","w",stdout); scanf("%lld%lld", &r, &c); if (r < c) swap( r, c );//lower the number of situations; printf("%lld\n", (r & 1 && c & 1) ? 0 : dp( r, c ) ); return 0;}
借我爸的一句话
非上上智, 不了了心。
- T解 POJ-2411 Mondriaan's Dream [轮廓线DP] || [状压DP]
- poj - 2411 - Mondriaan's Dream(轮廓线dp)
- poj 2411 Mondriaan's Dream 轮廓线dp
- POJ 2411 Mondriaan's Dream 轮廓线DP
- POJ 2411 Mondriaan's Dream(插头DP,轮廓线)
- POJ-2411 Mondriaan's Dream(轮廓线dp)
- [POJ] 2411 Mondriaan's Dream [轮廓线dp]
- POJ_P2411 Mondriaan's Dream(轮廓线DP)
- POJ 2411 Mondriaan's Dream (状压DP)
- Mondriaan's Dream - POJ 2411 状压dp
- POJ 2411 Mondriaan's Dream (状压DP)
- poj 2411 Mondriaan's Dream 状压dp
- poj 2411 Mondriaan's Dream(状压dp)
- POJ 2411 Mondriaan's Dream 状压dp
- POJ 2411 Mondriaan's Dream ( 状压DP )
- Mondriaan's Dream POJ - 2411 状压DP
- POJ 2411 Mondriaan's Dream (状压dp)
- POJ 2411 Mondriaan's Dream 状压dp
- 线程互斥
- CCCC训练练习题-矿工安全生产(求点割集)
- bash配置文件读取流程
- 228. Summary Ranges
- 剑指offer2:实现Singleton模式
- T解 POJ-2411 Mondriaan's Dream [轮廓线DP] || [状压DP]
- 【php】windows搭建apache、php、mysql环境
- Sublime 3 下安装 Package Control
- Linux创建配置使用SVN,可远程同步,可实时同步到web目录中
- Linux下创建、进入、查看至少需要什么权限
- JTAG与SWD引脚对比
- rabbitmq可靠确认模式的java封装及示例
- python数据处理工具包——pandas简介(如何构建数据结构篇)
- mysql连接失败 10038问题