P1879 [USACO06NOV]Corn Fields G (狀壓dp)

P1879 [USACO06NOV]Corn Fields G

洛谷鏈接

題目描述
Farmer John has purchased a lush new rectangular pasture composed of M by N (1 ≤ M ≤ 12; 1 ≤ N ≤ 12) square parcels. He wants to grow some yummy corn for the cows on a number of squares. Regrettably, some of the squares are infertile and can’t be planted. Canny FJ knows that the cows dislike eating close to each other, so when choosing which squares to plant, he avoids choosing squares that are adjacent; no two chosen squares share an edge. He has not yet made the final choice as to which squares to plant.

Being a very open-minded man, Farmer John wants to consider all possible options for how to choose the squares for planting. He is so open-minded that he considers choosing no squares as a valid option! Please help Farmer John determine the number of ways he can choose the squares to plant.

農場主John新買了一塊長方形的新牧場,這塊牧場被劃分成M行N列(1 ≤ M ≤ 12; 1 ≤ N ≤ 12),每一格都是一塊正方形的土地。John打算在牧場上的某幾格裏種上美味的草,供他的奶牛們享用。

遺憾的是,有些土地相當貧瘠,不能用來種草。並且,奶牛們喜歡獨佔一塊草地的感覺,於是John不會選擇兩塊相鄰的土地,也就是說,沒有哪兩塊草地有公共邊。

John想知道,如果不考慮草地的總塊數,那麼,一共有多少種種植方案可供他選擇?(當然,把新牧場完全荒廢也是一種方案)

輸入格式
第一行:兩個整數M和N,用空格隔開。

第2到第M+1行:每行包含N個用空格隔開的整數,描述了每塊土地的狀態。第i+1行描述了第i行的土地,所有整數均爲0或1,是1的話,表示這塊土地足夠肥沃,0則表示這塊土地不適合種草。

輸出格式
一個整數,即牧場分配總方案數除以100,000,000的餘數。

輸入輸出樣例
輸入 #1
2 3
1 1 1
0 1 0
輸出 #1
9

Solution

棋盤類狀壓dp

把每一行的擺放方法轉化成一個二進制數

首先, 也是最關鍵的一步: 先把一行中不可行的狀態(此處爲相鄰)預處理,可以節省枚舉的大量花費

然後在考慮不同行之間的狀態轉移,以合法的方式轉移
(此處的約束條件是相鄰,所以當前行受上一行的影響)

令dp[i][j]爲第i行狀態爲j的方案總數。

代碼

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int SZ = 4097;
const int MOD = 1e8;
int n,m,N,temp;
int dp[13][SZ],line[SZ],can[SZ];
int mp[13][13];

int main()
{
	scanf("%d%d",&m,&n);
	N = (1 << n) - 1;   
	for(int i = 1;i <= m;i ++ )
		for(int j = 1;j <= n;j ++ )
			scanf("%d",&mp[i][j]);
	for(int i = 1;i <= m;i ++ )//記錄同一行的合法狀態 
		for(int j = 1;j <= n;j ++ )
			line[i] = (line[i] << 1) + (mp[i][j]?0:1); //line 記錄的是當前行不能放的位置 1表示不能放 0表示能放 
	temp = 0;
	for(int i = 0;i <= N;i ++ )
	{
		if(i & (i >> 1)) continue;
		can[++ temp] = i; //行的可行狀態,保證同行的不相鄰 
	}
	int now;
	for(int i = 1;i <= temp;i ++ )
	{
		now = can[i];
		if(can[i] & line[1]) continue;//第一行初始化  不能放的位置直接跳過 
		dp[1][i] = 1;
	}
	int last;
	for(int i = 2;i <= m;i ++ )   
	{
		for(int j = 1;j <= temp;j ++ )
		{
			last = can[j];//上一行  (當前行的狀態由上一行所有可行狀態轉移到當前可行狀態) 
			if(last & line[i - 1]) continue; //上一行不可行 
			for(int k = 1;k <= temp;k ++ )
			{
				now = can[k];
				if(now & line[i]) continue;//當前行不可行 
				if(now & last) continue;//當前行與上一行衝突 
				dp[i][k] += dp[i - 1][j];
				dp[i][k] %= MOD; 
			}
		}
	}
	int ans = 0;
	for(int i = 1;i <= temp;i ++ )
	ans = (ans + dp[m][i]) % MOD;//ans = Σdp[m][i] 
	printf("%d\n",ans);
	return 0; 
} 
 

2020.4.7

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章