【POJ2411】Mondriaan's Dream(轮廓线DP)

【POJ2411】Mondriaan's Dream(轮廓线DP)

题面

Vjudge

题解

这题我会大力状压!!!
时间复杂度大概是\(O(2^{2n}n^2)\),设\(f[i][S]\)表示当前第\(i\)行向下伸展出去的状态为\(S\)
那么每次枚举一下当前行的放法,进行转移就好了。
然后就长成了这个样子(不要在意我强行缩减代码长度)
尽管这不是我们本题的重点,然而我还是放份代码

#include<cstdio>
#include<cstring>
int n,m;long long f[12][1<<11];
int main()
{
	while(scanf("%d%d",&n,&m))
	{
		if(n==0)break;memset(f,0,sizeof(f));f[0][0]=1;
		for(int i=1;i<=n;++i)
			for(int j=0;j<(1<<m);++j)
				for(int k=0;k<(1<<m);++k)
				{
					if(j&k)continue;
					int t=j|k;bool fl=true;
					for(int l=0;l<m;++l)
						if(!(t&(1<<l)))
							{
								if(t&(1<<(l+1))){fl=false;break;}
								if(l==m-1){fl=false;break;}
								t|=1<<l;t|=1<<(l+1);
							}
						if(fl)f[i][k]+=f[i-1][j];
				}
		printf("%lld\n",f[n][0]);
	}
}

标题里都写了是轮廓线\(dp\),那么我们就来轮廓线一下?
我们从上往下,从左往右依次放东西。假设当前填到了位置\((i,j)\)
那么对于当前以及接下来的状态有影响的只有\((i,1..j-1)\)\((i-1,j..m)\)
一共\(m\)个位置,那么我们把这些位置按照从上往下从左往右的顺序压缩。
考虑如何转移:
1.作为一个竖着放的矩形的下半部分,那么需要\((i-1,j)\)未被覆盖。
2.作为一个横着放的矩形的右半部分,那么需要\((i,j-1)\)未被覆盖。
3.啥都不干,等着后面的格子来覆盖当前位置。

当然,每一项转移都要限制,比如如果当前位置的正上方的位置是空的,那么必须竖着覆盖。
接下来就是大力的转移咯。

#include<cstdio>
#include<cstring>
int n,m;long long f[122][1<<11];
int main()
{
	while(scanf("%d%d",&n,&m)&&n)
	{
		memset(f,0,sizeof(f));f[0][(1<<m)-1]=1;
		for(int i=1;i<=n;++i)
			for(int j=1,nw=i*m-m+j;j<=m;++j,++nw)
				for(int k=0;k<(1<<m);++k)
					if(f[nw-1][k])
					{
						if(i>1&&!(k&1))
							f[nw][(k>>1)|(1<<(m-1))]+=f[nw-1][k];
						if(j>1&&!(k&(1<<(m-1)))&&(k&1))
							f[nw][(k>>1)|(1<<(m-1))|(1<<(m-2))]+=f[nw-1][k];
						if(((k&1)||i==1))
							f[nw][k>>1]+=f[nw-1][k];
					}
		printf("%lld\n",f[n*m][(1<<m)-1]);
	}
}


posted @ 2018-08-03 16:19  小蒟蒻yyb  阅读(509)  评论(1编辑  收藏  举报