洛谷P10975 Mondriaan's Dream
DP数组第二维是“轮廓线”的状态,即已知状态和未知状态的分界线
因为很显然,我们不关心已知状态和未知状态具体的方案如第1行第4列的方案数是多少,我们只关心“交界处”即“轮廓线”的方案,所以只需要保存轮廓线的状态即可
其中cur是上一个即已知的状态,nex即下一个未知的状态(当前为轮廓线)
注意,在状态的定义中,将\(1\)定义为不放置东西,在此处留一个“插头”,根据不同的题目情景,它将和不同位置的后续格子相连(本题中是和右边和下边的相连)
而\(0\)的定义则是放置东西,不留插头
所以很显然,边界条件为\(cur[0]=1\)(什么都不放),求\(cur[0]\)
#include<iostream>
#include<cstring>
using namespace std;
using ll=long long;
int h,w;
ll dp[2][1<<12];
int main(){
cin.tie(nullptr)->sync_with_stdio(false);
while(cin>>h>>w && h && w){
memset(dp,0,sizeof dp);
ll *cur=dp[0],*nex=dp[1];
//滚动数组优化,cur为已知方案数,nex是未知方案数
cur[0]=1;
for(int i=h-1;i>=0;i--)
for(int j=w-1;j>=0;j--){
for(int S=0;S<(1<<w);S++){
if((S>>j) & 1) nex[S]=cur[S^(1<<j)];
//若留插头,不放置,那么方案直接从放置的情况转移过来
else{
ll ans=0;
if(j+1<w && !((S>>(j+1))&1)) ans+=cur[S|1<<(j+1)];
//尝试横着放,那么很显然,从下一列(这里列的顺序可能是从右到左递增,我不是很清楚)有插头的地方匹配
if(i+1<h) ans+=cur[S|1<<j];
//尝试竖着放,那么很显然,从这一列有插头的地方匹配
nex[S]=ans;
}
}
swap(cur,nex);
}
cout<<cur[0]<<'\n';
}
}
浙公网安备 33010602011771号