题解 | 《算法竞赛进阶指南》 蒙德里安的梦想

【题目】

Squares and rectangles fascinated the famous Dutch painter Piet Mondriaan. One night, after producing the drawings in his ‘toilet series’ (where he had to use his toilet paper to draw on, for all of his paper was filled with squares and rectangles), he dreamt of filling a large rectangle with small rectangles of width 2 and height 1 in varying ways. Expert as he was in this material, he saw at a glance that he’ll need a computer to calculate the number of ways to fill the large rectangle whose dimensions were integer values, as well. Help him, so that his dream won’t turn into a nightmare!

【题意】

即给定一个 N × M N \times M 大小的图,然后问用无数个 1 × 2 1 \times 2 大小的砖头把这个图填充满的方案有多少个。

【题解】

因为数据范围十分的小,所以可以用状压来枚举单排所有的情况,然后dp到最后一排,这做法也就是状压dp。我们可以规定砖头的竖放的上部分为1,砖头的横放或者是竖放的下部分为0。这样的话,单排的情况就本题列数最多的情况,最小状压出来的就是 00000000000 00000000000 ,而最大的是 11111111111 11111111111 。而仔细想一下就会发现最后一行因为不能为竖放的上部分,也就是不能为1,那么最后一行的情况必定全都是0。又都能发现,当前 i i 行 只会与 i 1 i-1 行 有关,因为理论上 i 1 i-1 行 我们是已经规定好了摆放的方式了的,那么 i i 行 的摆放方式是受限于 i 1 i-1 行 的。例如 i 1 i-1 行 和 i i 行 上下对应的格子都是1,这种就肯定是冲突的,当前 i i 行 的这种情况肯定也是不允许的。所以我需要一开始的时候预处理出所有 i 1 i-1 行 的情况,即上一行如果是这样这样的,那当前行可以怎样怎样的。然后再是使用dp进行循环每一行枚举所有所有情况,让后进行状态转移。而状压的操作是用到了位运算的,在下面的代码中。i<(1<<m)便是可能性所状压出的二进制,一共有(1<<m)-1个;i>>j&1则是取j+1上的二进制,计算当 i 1 i-1 行 和 i i 行 或位运算后,0个数的情况,当0的个数是为偶数时,是允许的,而0的个数时奇数时,是不被允许的,我们需要记录判断合并后允许的结果。j&K i 1 i-1 行 和 i i 行 与运算后的情况,都是0才是被允许的。j|k即用到之前预处理的信息。而最后一行必定全都是0,所以 f [ n ] [ 0 ] f[n][0] 就是最终的结果。

时间复杂度: O ( 4 M N ) O(4^MN)

#include<iostream>
#include<cstring>
#include<sstream>
#include<string>
#include<cstdio>
#include<cctype>
#include<vector>
#include<queue>
#include<cmath>
#include<stack>
#include<list>
#include<set>
#include<map>
#include<algorithm>
#define fi first
#define se second
#define MP make_pair
#define P pair<int,int>
#define PLL pair<ll,ll>
#define lc (p<<1) 
#define rc (p<<1|1)    
#define MID (tree[p].l+tree[p].r)>>1
#define Sca(x) scanf("%d",&x)
#define Sca2(x,y) scanf("%d%d",&x,&y)
#define Sca3(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define Scl(x) scanf("%lld",&x)
#define Scl2(x,y) scanf("%lld%lld",&x,&y)
#define Scl3(x,y,z) scanf("%lld%lld%lld",&x,&y,&z)
#define Pri(x) printf("%d\n",x)
#define Prl(x) printf("%lld\n",x)
#define For(i,x,y) for(int i=x;i<=y;i++)
#define _For(i,x,y) for(int i=x;i>=y;i--)
#define FAST_IO std::ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
#define STOP system("pause")
#define ll long long
const int INF=0x3f3f3f3f;
const ll INFL=0x3f3f3f3f3f3f3f3f;
const double Pi = acos(-1.0);
using namespace std;
template <class T>void tomax(T&a,T b){ a=max(a,b); }  
template <class T>void tomin(T&a,T b){ a=min(a,b); }
int n,m;
ll f[12][1<<12];
bool in_s[1<<12];

int main(){
	while(~Sca2(n,m)&&n){
		for(int i=0;i<(1<<m);i++){
			bool cnt=0,has=0;
			for(int j=0;j<m;j++){
				if(i>>j&1) has|=cnt,cnt=0;
				else cnt^=1;
				in_s[i]=has|cnt?0:1;
			}
		}
		f[0][0]=1;
		for(int i=1;i<=n;i++)    // 第i行
			for(int j=0;j<(1<<m);j++){  // 第i行的状态
				f[i][j]=0;        //下面循环的是i-1行的状态
				for(int k=0;k<(1<<m);k++) if((j&k)==0&&in_s[j|k]) f[i][j]+=f[i-1][k];
			}
			
		Prl(f[n][0]);
	}
}
发布了39 篇原创文章 · 获赞 9 · 访问量 1957

猜你喜欢

转载自blog.csdn.net/qq_36296888/article/details/103057810