1. 程式人生 > >POJ2411(狀壓Dp,插頭Dp,數學公式)

POJ2411(狀壓Dp,插頭Dp,數學公式)

題目大意:有一個由n*m組成的地磚,現在拿1*2的地磚來鋪,要求必須鋪滿。求總方案數(n,m<=11)。

這是一道狀壓Dp和插頭Dp的經典題目。不過,或許很多人和我一樣,看到這種題目後,都會想到搜尋。可是搜尋的時間複雜度是O(2^nm),肯定超時了。

【解法一:狀壓Dp】

首先設f[i][j]為前i行且第i行狀態為j時的方案總數。那麼f[i][j] = sum(f[i-1][j'] | j'能到達j);反過來,對於f[i][j],它可以向f[i+1][j'']貢獻(其中j能到達j‘’)。

那樣就可以設鋪的為1,不鋪的為0。拿1*2的地磚來鋪,無非就是橫鋪或者豎鋪。顯然:如果第i行的第k位不鋪,又由於必須鋪滿,所以第i+1行的第k為必須鋪。也就是說,如果把狀態j取反,二進位制中為1的那一個位置就必須豎著鋪。

所以,對於每一行的狀態j,可以用DFS來算出j能到達的狀態,則這個演算法的時間複雜度為O(n*2^2m)。

小優化:若n和m都是奇數,則答案為0。

證明:因為 n,m為奇數,

         不妨設:n=2q-1,m=2p-1(q,p為正整數),

           所以:nm=(2q-1)(2p-1) = 4qp - 2q - 2p + 1

           又因為:4qp,2q,2p為偶數,

           所以:nm為奇數。

因為是拿1*2的地磚鋪,且必須鋪滿。

所以nm是偶數,矛盾,正畢。

附上程式碼:

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;
#define Maxn 14

int sta[1<<Maxn];
long long f[Maxn][1<<Maxn];
int n,m,fp,p;

void dfs(int num,int p)
{
	if(p+1==m)
	{
		sta[fp++] = num;
		return;
	}
	dfs(num,p+1);//不鋪 
	
	if((num&(1<<p)) || (num&(1<<(p+1)))) return;
	dfs(num|(3<<p),p+1);//橫著鋪 
}

int main()
{
	while(scanf("%d%d",&n,&m) && n && m)
	{
		memset(f,0,sizeof(f));
		if(n*m%2)//小優化 
		{
			printf("0\n");
			continue;
		}
		fp=0;
		dfs(0,0);//預處理第一行 
		for(int i=0;i<fp;i++)
			f[1][sta[i]] = 1;
		
		p = 1<<m;
		for(int i=1;i<n;i++)
		for(int j=0;j<p;j++)//列舉當前狀態j 
		{
			fp = 0;
			dfs(p-1-j,0);
			for(int k=0;k<fp;k++)//列舉j能到達的狀態 
				f[i+1][sta[k]] += f[i][j]; 
		}
		cout<<f[n][p-1]<<endl;
	}
	return 0;
}

【解法二:插頭Dp】:

設f[i][j][k]表示當前列舉到第i行第j列的地磚,且前面m個地磚(不包括(i,j))的狀態為k(k用二進位制表示,1表示有,0表示沒有)。則有三種情況。

(1):不放,當且僅當k的第m位為1,才可以轉移狀態;

(2):往左放,當k的第m位為1,k的第1位為0且j不在最左時,可轉移;

(3):往上放,當k的第m位為1且i不在最上面的一行時,轉移;

我們可以根據上面的特點,用二進位制表示出來。

因為每一個(i,j)只和上一次有關,則可以用滾動陣列優化空間。

時間複雜度O(nm*2^m)。

程式:

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
#define Maxn 14

long long f[2][1<<Maxn];//注意long long 
int n,m,p,cur;

void change(int x,int y)//狀態轉移 
{
	if(y & (1<<m))
	f[cur][y^(1<<m)] += f[cur^1][x];
}

int main()
{
	while(scanf("%d%d",&n,&m) && n && m)
	{
		if(n*m%2)
		{
			printf("0\n");
			continue;
		}
		if(n<m) swap(n,m);//小優化 
		memset(f,0,sizeof(f));
		
		int p = 1<<m;
		f[cur=0][p-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<p;k++)
			{
				change(k,k<<1);//不放 (第k位一定為1)
				if(i && !(k&1<<m-1)) change(k,(k<<1) ^ (1<<m) ^ 1);
         //往上放 (注意到“1<<m”,滿足第k位一定為0,才是合法的,因為change函式進行轉移時必須滿足"y&(1<<m)",然後y|(1<<m)可以去掉它)
				if(j && !(k&1)) change(k,(k<<1)^3);//往左放
			}
		}
		printf("%lld\n",f[cur][p-1]);
	}
	return 0;
}

數學公式可以做:即,但我們知道c++的三角函式和開方有碩大的浮點誤差,或許產生精度問題。

相關推薦

POJ2411Dp插頭Dp數學公式

題目大意:有一個由n*m組成的地磚,現在拿1*2的地磚來鋪,要求必須鋪滿。求總方案數(n,m<=11)。 這是一道狀壓Dp和插頭Dp的經典題目。不過,或許很多人和我一樣,看到這種題目後,都會想到搜尋。可是搜尋的時間複雜度是O(2^nm),肯定超時了。 【解法一:狀壓

poj2411 Mondriaan's Dream dp+多米諾骨牌問題

這道題的解析這個部落格寫得很好 大致意思就是我們可以只處理兩行之間的關係,然後通過這兩個關係推出所有行(有點像矩陣快速冪的思想) 幾個要注意的地方 (1)第0行為全1 (2)發現自己的思維習慣還是先行在狀態,我自己寫得時候老是寫反。 (3)path的個數可能有很

Forming Quiz Teams dp最優配對問題

You have been given the job of forming the quiz teams for the next ‘MCA CPCI Quiz Championship’. There are 2 ∗ N students interested to

HDU58232016多校第八場——color II dp獨立集

First line contains an integer t. Then t testcases follow.  In each testcase: First line contains an integer n. Next n lines each contains a string consis

poj2411 Mondriaan's Dreamdp

用1*2的小木塊覆蓋n*m的格子,問方案數。n,m<=10.狀壓dp,用二進位制數表示一行的狀態,逐行轉移。橫放用11表示,豎放用01表示。a[s]存的是上一行狀態為s,能轉移到的所有狀態。初始值f[0][tot]=1,表示第0行全為1的方案有1個。最後答

SRM709 div1 Xscoregamedp

壓縮 out find 初始 reg max algo pan 是我 題目大意: 給定一個序列a,包含n個數(n<=15),每個數的大小小於等於50 初始時x = 0,讓你每次選a中的一個數y,使得x = x + x^y 問如何安排選擇的次序,使得最終結果最大。

lightoj 1057 - Collecting Golddp

define har ring volume www lightoj cst con continue 題目鏈接:http://www.lightoj.com/volume_showproblem.php?problem=1057 題解:看似有點下記憶話搜索但是由於

dpABC 067 F : Mole and Abandoned Mine

技術分享 cts using sub src oops bit live set Mole decided to live in an abandoned mine. The structure of the mine is represented by a simple

dpUVA - 1252 Twenty Questions

ace code return uva typedef clu http 狀壓 .net 題目地址 讀入二進制數及轉換的方法。 e.g. bitset<16> x;   cin>>x;   cout<<x.to_ulong()<&l

POJ 2288 Islands and Bridgesdp

pen clas tdi pac pre 初始 路徑 nds queue http://poj.org/problem?id=2288 題意: 有n個島嶼,每個島嶼有一個權值V,一條哈密頓路徑C1,C2,...Cn的值為3部分之和: 第1部分,將路徑中每個島嶼的權值累

dpNOI 2001POJ 1185 炮兵陣地

上下 數據 enter 能夠 sam src max spa 參加 司令部的將軍們打算在N*M的網格地圖上部署他們的炮兵部隊。一個N*M的地圖由N行M列組成,地圖的每一格可能是山地(用"H" 表示),也可能是平原(用"P"表示),如下圖。在每一格平原地形上最多可以布置一支炮

dpHDU 4778 Gems Fight!

ont magic fig urn turn sid several desc ant Gems Fight! Time Limit: 20000/10000 MS (Java/Others) Memory Limit: 327680/327680 K (Java/O

[luoguP1896] [SCOI2005]互不侵犯KingDP

放置 puts turn true i++ bsp pri += light 傳送門 先預處理出來一行中放置國王的所有情況和每種情況所用的國王個數。 f[i][j][k]表示前i行放j個國王且最後一行的狀態為k的方案數 狀壓DP即可 #include &l

[luoguP2704] 炮兵陣地DP

pro 狀態 blank show char %d har void href 傳送門 可以事先把每一行的所有狀態處理出來,發現每一行的狀態數最多不超過60個 f[i][j][k]表示前i行,第i行為狀態j,第i-1行為狀態k的最優解 #include

[luoguP3694] 邦邦的大合唱站隊/簽到題DP

pac sizeof size 前綴和 for ensure -m open https 傳送門 來自kkk的題解: 70分做法:枚舉每個學校順序,暴力。 100分:狀壓dp。從隊列頭到尾DP, 狀態:f[i]表示i狀態下最小的出列(不一致)的個數。 比如f

[luoguP2831] 憤怒的小鳥DP

狀壓 集合 blank tin 處理 cstring tps www. cpp 傳送門 感覺這題不是很難,但是很惡心。 說一下幾點。 1.預處理出來每兩個點所構成的拋物線能消除的豬的集合。 2.如果兩個點橫坐標相同,則不能構成拋物線 3.a >= 0

刷題總結——bzoj1725dp

數據 void clip esp pos 範圍 -c ble include 題目: 題目描述 Farmer John 新買了一塊長方形的牧場,這塊牧場被劃分成 N 行 M 列(1<=M<=12; 1<=N<=12),每一格都是一塊正方形的土地。

HDU 6149 Valley Numer IIDP

狀壓dp pac aps 狀態 01背包 using 百度 c++ 由於 題目鏈接 HDU6149 百度之星復賽的題目……比賽的時候並沒有做出來。 由於低點只有15個,所以我們可以考慮狀壓DP。 利用01背包的思想,依次考慮每個低點,然後枚

Hdu1074DoingHomeworkDP

cst cvs shuf shu lns oci docs 順子 lan eyffwb媒臼澇倩壓詞http://jz.docin.com/cmhc492k1c2qg站附夷揖儷手http://jz.docin.com/eguy6137k1s31e思廖智資粱迸http://sh

hihocoder #1608 : Jerry的奶酪dp

num cnblogs min ans namespace ios 題解 while mes 題目鏈接:http://hihocoder.com/problemset/problem/1608 題解:就是一道簡單的狀壓dp由於dfs過程中只需要幾個點之間的轉移所以只