轮廓线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;
}

猜你喜欢

转载自blog.csdn.net/guoshiyuan484/article/details/82937777