1. 程式人生 > >輪廓線dp-poj2411-鋪磚問題

輪廓線dp-poj2411-鋪磚問題

/*
我們要求什麼?
計數。
嗯,dp,貌似是個好的辦法。
數目由什麼確定?
不太知道~。
不過我們好像做過相似的題目,比如“開關問題"
鋪的磚受上下左右的磚塊的影響,我們強行只考慮右和下的方向,就和開關問題,這樣有助於簡化一下問題
也就是一塊磚隻影響他的右和下的方向鋪磚時候'->'這樣鋪和向下鋪,而不是'<-'這樣鋪和向上鋪。
我們再考慮下一個稍微簡單的問題,怎樣才能鋪成功呢,在不考慮計數的情況下,由開關問題,我們知道這隻和最下邊的那些邊緣是怎樣的有關,
比如當我們把前面的所有的空白都鋪滿了,但是最後有兩個豎直鋪的磚中間只相隔一個空格的話,我們無論如何都無法鋪成功的。
所以問要維護的是關於邊緣的一些資訊。
比如
---+****
*** 
如上'-’是我們已經鋪好的一部分不考慮了,
'+’是我們正在考慮怎樣鋪的磚,'*'是以後待考慮的
我們維護的就是關於‘+’和'*'位置的資訊
'+‘有兩種考慮的情況
1.受以前鋪的磚的影響,我們不能在'+'處鋪磚,那麼除了不在'+'處鋪磚外,我們一定要注意'+’處的下面,和右面是不受'+'的影響的
2.如果'+'處可以鋪磚,那麼是橫著鋪,還是豎著鋪?很明顯只要不是最後一排都可以豎著鋪,如果不是最後一列,而且右面那個'*'的地方沒受到上面鋪的磚的影響的話可以
橫著鋪
這樣就可以dp了,在一個位置‘0’表示不受以前鋪的磚的影響,'1'表示受到影響。
從上到下,從左到右考慮。
每考慮一個位的時候考慮怎樣鋪,然後把受影響的位置為1,沒受影響的置為'0'
比如01000,我們考慮到第二位
那麼我們是不能鋪磚的
這樣下面和右面都不受影響
我們把第二位,和第三位置0
為什麼要把第二位置’0‘因為在下一排的時候我們在考慮這一列的時候就知道他是否受到了上面的磚的影響了。
其他同理。
最後先開始
010000 和100000這兩個狀態是1,其他都為0 ,代表我們只先考慮了左上角的第一個位置鋪的磚的情況,
分別代表橫著和豎著
然後
在最後
最後那個位為'1'的磚才會被鋪,所以如果那個位為'0'就不計數。
*/
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef unsigned long long ll;
ll dp[2][1 << 20];
int h, w;
ll anss[20][20];
ll func(int n, int m)
{
	if (n == 0 || m == 0)
		return 0;
	if (n == 1)
	{
		if (m & 1)
			return 0;
		else
			return 1;
	}
	if (m == 1)
	{
		if (n & 1)
			return 0;
		else
			return 1;
	}
	ll ans = 0;
	for (int j = 0; j < (1 << m); j++)
		dp[0][j] = 0;
	dp[0][1 << (m - 1)] = 1;
	dp[0][1 << (m - 2)] = 1;
	int temp,know;
	know = 0;
	for (int j = m - 2; j >= 0; j--)
	{
		int temp = know ^ 1;
		for (int k = 0; k < (1 << m); k++)
			dp[temp][k] = 0;
		for (int k = 0; k < (1 << m); k++)
		{
			int nextt;
			if (k&(1 << j))
			{
				nextt = k&(~(1 << j));
				if (j)
				nextt &= (~(1 << (j - 1)));
				dp[temp][nextt] += dp[know][k];
			}
			else
			{
				nextt = (k | (1 << j));
				if (j)
				{
					nextt &= (~(1 << (j - 1)));
					int nextt1 = k | (1 << (j - 1));
					dp[temp][nextt1] += dp[know][k];
				}
				dp[temp][nextt] += dp[know][k];
			}
		}
		 know=temp;
	}
	for (int i =1; i<n; i++)
	{
		for (int j = m - 1; j >= 0; j--)
		{
			if (i == n - 1 && j == 0)
			{
				for (int k = 0; k < (1 << m); k++)
					if ((k&1))
					ans += dp[know][k];
				
			}
			temp = know ^ 1;
			for (int k = 0; k < (1 << m); k++)
				dp[temp][k] = 0;
			for (int k = 0; k < (1 << m); k++)
			{
				if (k&(1 << j))
				{
					int nextt = k&(~(1 << j));
					dp[temp][nextt] += dp[know][k];
				}
				else
				{
					if (i != n - 1)
					{
						int nextt = k | (1 << j);
						dp[temp][nextt] += dp[know][k];
					}
					if (j&&(!(k&(1<<(j-1)))))
					{
						int nextt2 = k|(1<<(j - 1));
						dp[temp][nextt2] += dp[know][k];
					}
					
				}
			}
			know = temp;
		}
	}
	return ans;
}
int main()
{
	while (scanf("%d%d", &w, &h))
	{
		if (w > h)
			swap(w, h);
		if (w == 0 && h == 0)
			break;
		if (anss[h][w])
			printf("%llu\n", anss[h][w]);
		else
		{
			anss[h][w] = func(h, w);
			printf("%llu\n", anss[h][w]);
		}
	}
	return 0;
}