poj2411 解题报告

题目:给你一个h*w的矩形,用一个1*2的小矩形去填充,问有多少种填充方法,不考虑对称性

详细的解题报告参考:

http://www.2cto.com/kf/201208/146894.html

下面描述代码实现

#include <stdio.h>
#include <string.h>

#define swap(x, y) do { \
	int t; \
	t = x; \
	x = y; \
	y = t; \
}while(0) \

#define MAXN 1<<11

long long  f[2][MAXN];
long long  ans[12][12];
int width;

int test11(int x) 
{
	while (x) {
		if (x&0x1) {
			if (x&0x2) 
				x >>= 2;
			else
				return 0;
		} else {
			x >>= 1;
		}
	}
	return 1;
}

int testState(int x, int y)
{
	if ((x|y) != width-1) return 0;
	return test11(x&y);
}

int main()
{
	int m, n;
	int i, j, k;
	memset(ans,-1,sizeof(ans));
	while (scanf("%d %d", &m, &n) && (m!= 0 || n!=0)) {

if(ans[m][n] != -1) {

printf("%lld\n", ans[m][n]); continue; } if (m < n) swap(m, n); if (n == 0 || (0x1&m&n) == 1) { printf("0\n"); ans[m][n]=ans[n][m]=0; continue; } width = 1<<n; for (i = 0; i < 2; i++) for (j = 0; j < width; j++) f[i][j] = 0; for (i = 0; i < width; i++) { if (test11(i)) f[0][i] = 1; } /* for (i = 1; i < m; i++) { for (j = 0; j < width; j++){ long long tmp = 0; for (k = 0; k < width; k++) { if(testState(j, k)) tmp += f[(i-1)%2][k]; } f[i%2][j] = tmp; } } */ /*剪枝优化*/ for (i = 1; i < m; i++) { memset(f[i%2], 0, sizeof(f[0])); for(k = 0; k < width; k++) { if (!f[(i-1)%2][k]) continue; for (j = 0; j < width; j++) { if (testState(j, k)) f[i%2][j] += f[(i-1)%2][k]; } } } ans[m][n]=ans[n][m]=f[(m-1)%2][width-1]; printf("%lld\n", ans[m][n]); } return 0; }

解题思路给出的把铺砖问题转换成填0和1的问题,还是不太明白怎么想到,竖着的要填0,1。。。。

test11函数用于判断一个状态数字的二进制表示中,连续的1为偶数个,该函数用于初始化第一行,同时也可以用于其他行状态的判断
testState函数实现判断当前行的状态是否满足条件(通过和前一行状态比较),根据解题思路中的兼容性规则,我们可以知道上下两行对应的数字是01,10,11,因此不可能是00,也就是说x|y的值肯定是width-1,而且x&y后的值满足连续的1是偶数个,这可以调用test11判断。
由于DP的转移函数只与前一行有关,因此可以使用滚动矩阵来降低空间复杂度,因为需要做累加操作,使用前需要清空原先的行。
main函数中的三个for语句实现了剪纸优化,优化前使用/**/中的语句,可以看到当f[(i-1)%2][k]等于0时,f[i%2][j]的值不会变化,可以剪去;且内部两个for语句调换顺序并不会影响程序执行结果,因此可以先遍历k,且只有在f[(i-1)%2][k]!=0时才遍历j。
代码还使用了ans[12][12]来存储计算结果,后续相同的m*n就可以直接使用索引ans
程序编写的过程中出现三个问题:
1. 输出结果可能会很大,因此需要用long long类型
2. f[][]改成滚动矩阵f[2][]时,起初没有考虑清除前一行的数据,导致结果不正确
3. 使用ans优化时,起初申明了11*11的全局变量ans[11][11],由于编译器越界时不会报错,且由于是全局变量,竟然可以使用ans[11][10]这样的值,只是poj一直给出wrong answer的错误,最后才发现是越界了。

POJ1753题目为"Flip Game",题目给出了一个4x4的棋盘,每个格子有黑色或白色,每次翻转一个格子会同时翻转它上下左右四个格子的颜色,目标是把整个棋盘都变为同一种颜色,求把棋盘变成同种颜色的最小步数。 解题思路: 一般关于棋盘变色的题目,可以考虑使用搜索来解决。对于POJ1753题目,可以使用广度优先搜索(BFS)来解决。 首先,对于每个格子,定义一个状态,0表示当前格子是白色,1表示当前格子是黑色。 然后,我们可以把棋盘抽象成一个长度为16的二进制数,将所有格子的状态按照从左往右,从上往下的顺序排列,就可以用一个16位的二进制数表示整个棋盘的状态。例如,一个棋盘状态为: 0101 1010 0101 1010 则按照从左往右,从上往下的顺序把所有格子的状态连接起来,即可得到该棋盘的状态为"0101101001011010"。 接着,我们可以使用队列来实现广度优先搜索。首先将初始状态加入队列中,然后对于队列中的每一个状态,我们都尝试将棋盘上的每个格子翻转一次,生成一个新状态,将新状态加入队列中。对于每一个新状态,我们也需要记录它是从哪个状态翻转得到的,以便在得到最终状态时能够输出路径。 在搜索过程中,我们需要维护每个状态离初始状态的步数,即将该状态转换为最终状态需要的最小步数。如果我们找到了最终状态,就可以输出答案,即最小步数。 代码实现:
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值