您的位置:首页 > 其它

poj2411 Mondriaan's Dream(轮廓线动态规划)

2015-03-23 00:35 417 查看
【题解】

首先将棋盘转化成"竖长形"(m<=n),然后从上往下逐行推进、每行从左往右枚举放骨牌的格子(规定骨牌只能往上、左两个方向放,不会影响到尚未枚举的格子)

那么正枚举的当前行可能是参差不齐的,在当前行产生轮廓(用1/0表示当前是否被覆盖,若当前格选择填0,其上方相邻位必为1,否则再无法被覆盖)

以枚举格的坐标和当前行的"轮廓"为状态,即:

d[cur][k]:棋盘第cur个格在状态为k时的方案数

其中,棋盘格位置(i,j)用cur来代替(相当于d[i][j][k],而d[i][j][k]只与(i,j-1)有关,因此采用滚动数组)

k是一个m位的二进制数,利用状压来表示(i,j-1),(i,j-2),…,(i,1),(i-1,m),(i-1,m-1),…,(i-1,j)是否被覆盖,这确定了一个封闭轮廓,记为"Km,…K2,K1"

(i,j)有三种决策:

不放骨牌:(i-1,j)必须有骨牌(Km==1),状态:1,…,K2,K1 (对(i,j)来说) -> K(m-1)…K2,K1,0 (对(i,j+1)或j==m时(i+1,1)来说)

放朝上的骨牌:(i-1,j)必须无骨牌(i!=1&&Km==0),状态:0,K(m-1),…,K2,K1 -> K(m-1)…K2,K1,1

放朝左的骨牌:(i-1,j)必须有骨牌,(i,j-1)必须无骨牌(j!=1&&Km==1&&K1==0),状态:1,K(m-1),…,K2,0 -> K(m-1)…K2,1,1

【代码】

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
long long d[2][4000]={0};
void jh(int* a,int* b)
{
	int t=*a;
	*a=*b;
	*b=t;
}
int main()
{
	int n,m,i,j,k,cur;
	while(scanf("%d%d",&n,&m)==2&&n!=0)
	{
		if(n<m) jh(&n,&m);
		cur=0;
		d[0][(1<<m)-1]=1;/*为什么其他的d[0][k]不用赋0:若k的二进制位Ki为0,则d[0][k]只对(1,Ki)上放朝上的骨牌起作用,
		但第一行不能选择"放朝上的骨牌",因此d[1]["Ki朝上放骨牌对应的状态"]并未被改变,还是0,当然也不会影响接下来的几行*/
		for(i=1;i<=n;i++)
			for(j=1;j<=m;j++)
			{
				cur^=1;
				memset(d[cur],0,sizeof(d[cur]));
				for(k=0;k<(1<<m);k++)
				{
					if( k&(1<<m-1) ) d[cur][(k<<1)^(1<<m)]+=d[1-cur][k];//不放骨牌 
					if( i!=1 && (k&(1<<m-1))==0 ) d[cur][(k<<1)+1]+=d[1-cur][k];//放朝上的骨牌 
					if( j!=1 && (k&(1<<m-1)) && (k&1)==0 ) d[cur][(k<<1)^(1<<m)+3]+=d[1-cur][k];//放朝左的骨牌 
				}
			}
		printf("%lld\n",d[cur][(1<<m)-1]);
	}
	return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: