POJ2411 Mondriaan's Dream 状压+轮廓线dp

传送门

Sol

首先状压大概是很容易想到的

一般的做法大概就是枚举每种状态然后判断转移

但是这里其实可以轮廓线dp

也就是从上到下,从左到右地放方块

假设我们现在已经放到了$(i,j)$这个位置

那么影响这个位置怎么填的其实就只有这个位置上面的位置到它左边的位置这一段的状态

于是把这一段从上到下从左往右状压起来,1表示被覆盖了,0表示没被覆盖

$f[i][j][s]$表示填到第$(i,j)$,$(i-1,j)$到$(i,j-1)$的状态为s 的方案数

转移:

原则是要把现在考虑的一行的上一行填满,不然它就永远不会被覆盖了

若$(i-1,j)=0$,即上面的格子没放,这时只能放竖起来的方块

若$(i-1,j)=1,(i,j-1)=0$即上面的格子放了,左边的格子没放,这时可以放横着的方块

若$(i-1,j)=1$即上面的格子放了,这时可以不放方块

最后要注意第一行的状态

Code

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #define Ri register int
 5 #define ll long long
 6 #define mem(a,b) memset(a,b,sizeof(a))
 7 using namespace std;
 8 int n,m;
 9 ll f[130][1<<11];
10 int main()
11 {
12     while(1)
13     {
14         scanf("%d%d",&n,&m);
15         if(!n||!m)break;
16         mem(f,0);f[0][(1<<m)-1]=1;
17         for(Ri i=1;i<=n;i++)
18             for(Ri j=1,t=m*(i-1)+j;j<=m;j++,t++)
19                 for(Ri k=0;k<(1<<m);k++)
20                 if(f[t-1][k])
21                 {
22                     ll hhh=f[t-1][k];
23                     if(i>1 && !(k&1))
24                         f[t][(k>>1)|(1<<(m-1))]+=hhh;
25                     if(j>1 && !(k&(1<<(m-1))) && (k&1))
26                         f[t][(k>>1)|(1<<(m-1))|(1<<(m-2))]+=hhh;
27                     if(i==1||k&1)
28                         f[t][k>>1]+=hhh;
29                 }
30         printf("%lld\n",f[n*m][(1<<m)-1]);
31     }
32     return 0;
33 }

猜你喜欢

转载自www.cnblogs.com/forward777/p/10958581.html