1. 程式人生 > 實用技巧 >P1879 [USACO06NOV] Corn Fields G

P1879 [USACO06NOV] Corn Fields G

題目描述

農場主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

一道入門的狀壓dp題,但對於我這種萌新還是太難了,所以寫篇部落格紀念一下。。。

看到m的範圍很小,我們就可以考慮對m進行狀態壓縮

我們設 \(f[i][j]\) 表示第\(i\)行放置情況為\(j\)的方案數.

我們就可以列舉所有的狀態來進行轉移

也就是 \(f[i][j] += f[i-1][k]\)

那怎麼判斷一個狀態是否合法呢?

首先,這一行不能選相鄰的兩列也就是 j & (j<<1) == 0, j & (j>>1) == 0

並且還要與上一行的所選的不能有相鄰的即 j & k == 0

那麼怎麼判斷這個狀態選的土地是否都合法呢?

我們可以預先處理每一行的初始狀態,即對土地能不能選進行狀態壓縮。

當 s[i] & j == 0 就代表j這個狀態可能選了不肥沃的土地。(可以畫個圖瞭解一下)

最終答案就是 \(\sum_{i=0}^{i< (1<<m)} f[n][i]\)

程式碼

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int p = 1e8;
int n,m,ans,x,base[20],f[15][35000],zhuangtai[20],map[20][20];
inline int read()
{
	int s = 0, w = 1; char ch = getchar();
	while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9'){s = s * 10+ch -'0'; ch = getchar();}
	return s * w; 
}
bool judge(int x)//判斷x這個狀態是否合法
{
	if(x & (x<<1)) return 0;
	if(x & (x>>1)) return 0;
	return 1;
}
int main()
{
	n = read(); m = read();
	for(int i = 1; i <= n; i++)
	{
		for(int j = 1; j <= m; j++)
		{
			map[i][j] = read();
		}
	}
	for(int i = 1; i <= n; i++)
	{
		for(int j = 1; j <= m; j++)
		{
			zhuangtai[i] = (zhuangtai[i]<<1) + map[i][j];//把一開始土地能不能選的情況進行狀態壓縮
		}
	}
	base[0] = 1;
	for(int i = 1; i <= m; i++) base[i] = base[i-1] * 2;
	f[0][0] = 1;
	for(int i = 1; i <= n; i++)
        {
		for(int j = 0; j < base[m]; j++)
		{
			if(judge(j) && ((zhuangtai[i] & j) == j))//j這個狀態不能選不肥沃的土地
			{
				for(int k = 0; k < base[m]; k++)
				{
					if(((j&k) == 0) && judge(k))
					{
						f[i][j] += f[i-1][k];//轉移
						f[i][j] %= p;
					}
				}
			}
		}
	}
	for(int i = 0; i < base[m]; i++) ans = (ans + f[n][i]) % p;
	printf("%d\n",ans);
	return 0;
}