1. 程式人生 > >[poj3254]Corn Fields_狀壓dp

[poj3254]Corn Fields_狀壓dp

nbsp 可能 運用 局限 scanf field efi string 調試

Corn Fields poj3254

    題目大意:給你一個n*m的地,每一塊地可以種或不種,兩塊種過的地不能挨著,可以一塊都不種,問所有的種地方案數。

    註釋:讀入用0和1,1<=n,m<=12.

      想法:這題和炮兵陣地特別像,比炮兵更簡單。我們再度入的時候直接處理出當前行的地的不可種的情況。預處理出一行如果都能種的話所可能的方案數。此處需要滿足的就是兩塊地不能挨著,通過打表我們可以發現這種情況最多只有377種情況(我們附上打表用的程序)

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
bool check(int x)//判斷當前狀態是否可行
{
	if(x&(x<<1)) return false;
	return true;
}
int cnt;
void before(int mid)//處理所有狀態
{
	for(int i=0;i<(1<<mid);i++)
	{
		if(check(i)) cnt++;
	}
}
int main()
{
	int n;
	cin >> n;
	before(n);
	printf("%d\n",cnt);//輸出所有可行狀態數
	return 0;
}

      然後,我們先預處理出第一行,怎麽處理呢?其實map是0的情況,也就是讀入數據的反碼。我們對於一個狀態只需要通過&上map對應的下標就可以判斷當前數據是否合法。如果合法,dp[1][i]就是1。其中,dp[i][j]表示第 i 行狀態為 j ,前 i 行能填充的方案數。轉移時,我們通過外層松弛行數,內層枚舉所有狀態,用&判斷即可。

    最後,附上醜陋的代碼... ...

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define mod 1000000000
using namespace std;
int dp[15][380];
int map[15];//存儲的是反碼
int str[380];//存儲所有狀態
// int sum[350];
int cnt;//狀態數目
bool check(int x)//判斷當前狀態是否可行
{
	if(x&(x<<1)) return false;
	return true;
}
// int getSum(int x)
// {
// 	int ans=0;
// 	while(x>0)
// 	{
// 		if(x&1) ans++;
// 		x>>=1;
// 	}
// 	return ans;
// }
void before(int mid)//預處理所有狀態
{
	for(int i=0;i<(1<<mid);i++)
	{
		if(check(i))
		{
			str[++cnt]=i;
			// sum[cnt]=getSum(i);
		}
	}
}
int main()
{
	int n,m;
	cin >> n >> m;
	int a;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			scanf("%d", &a);
			if(a==0) map[i]|=(1<<(j-1));//運用|運算的性質來構造反碼
		}
	}
	before(m);//預處理
	// cout << "cnt = " << cnt << endl;
	for(int i=1;i<=cnt;i++)
	{
		if(!(str[i] & map[1]))
		{
			dp[1][i]=1;
		}
	}
	// for(int i=1;i<=n;i++) printf("%d ",map[i]);
	// puts("");
	// for(int i=1;i<=cnt;i++) printf("%d ",dp[1][i]);
	// puts("");
	for(int i=2;i<=n;i++)//轉移
	{
		for(int j=1;j<=cnt;j++)//枚舉i的狀態
		{
			if(str[j] & map[i]) continue;//判斷狀態是否合法
			for(int k=1;k<=cnt;k++)//枚舉i-1的狀態
			{
				if(str[k] & map[i-1]) continue;
				if(str[j] & str[k]) continue;
				dp[i][j]+=dp[i-1][k];
			}
		}
	}
	int ans=0;
	for(int i=1;i<=cnt;i++)
	{
		if(map[n]&str[i]) continue;
		ans+=dp[n][i];
		ans%=mod;//重要,不加luogu會WA 10%
	}
	printf("%d\n",ans);
	return 0;
}

    小結:第2道狀壓。調試時候不要忘記題目所求的,在發現一個題與另一個題相似時不要被另一道題所局限

[poj3254]Corn Fields_狀壓dp