洛谷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';
	}
}

posted on 2026-02-02 11:18  _CENSORED  阅读(3)  评论(0)    收藏  举报

导航