1. 程式人生 > >[poj1185]炮兵陣地_狀壓dp

[poj1185]炮兵陣地_狀壓dp

check using tin cst OS con sof ios ++

炮兵陣地 poj-1185

    題目大意:給出n列m行,在其中添加炮兵,問最多能加的炮兵數。

    註釋:n<=100,m<=10。然後只能在平原的地方建立炮兵。

      想法:第2到狀壓dp,++。這題顯然是很經典的。設狀態dp[i][j][k]表示第i行的狀態為j,i-1行的狀態為k的最多炮兵數。在轉移時,枚舉所有的合法炮兵排列(此處的合法數目是根據一行全為平原的時候能放置的合法炮兵數目),然後內層循環枚舉dp[i-1]的i-1狀態,進行特判更新即可。統計答案時,我們只需對於dp[n]的所有可能狀態求最大值即可。

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

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int dp[105][65][65];
int cnt=0;
int str[65];
int sum[65];
int map[110];
char s[110][110];
bool check(int x)//判斷這個排列是否為普通的合法序列
{
	if(x&(x<<1)) return false;
	if(x&(x<<2)) return false;
	return true;
}
int getSum(int x)//當前狀態的炮兵數目
{
	int ans=0;
	while(x>0)
	{
		if(x&1) ans++;
		x>>=1;
	}
	return ans;
}
void before_hand(int mid)//預處理出所有的可能合法炮兵狀態,cnt統計狀態數
{
	for(int i=0;i<(1<<mid);i++)
	{
		if(check(i))
		{
			str[cnt]=i;//str數組記錄狀態,dp中的j和k都是str數組的下標
			sum[cnt]=getSum(i);
			cnt++;
		}
	}
}
int main()
{
	int n,m;
	scanf("%d%d",&n,&m);
	memset(dp,-1,sizeof dp);
	for(int i=0;i<n;i++)
	{
		for(int j=0;j<m;j++)
		{
			char a;
			cin >> a;
			if(a==‘H‘) map[i]|=(1<<j);//統計每一行的不合法格子狀態
		}
	}
	before_hand(m);//其實可以不傳參
	for(int i=0;i<cnt;i++)
	{
		if(!(str[i]&map[0])) dp[0][0][i]=sum[i];//先處理出第一行的情況
	}
	for(int r=1;r<n;r++)//枚舉行數
	{
		for(int i=0;i<cnt;i++)//枚舉當前行的排列
		{
			if(str[i]&map[r]) continue;
			for(int j=0;j<cnt;j++)//枚舉上一行的排列情況
			{
				if(str[i]&str[j]) continue;
				for(int k=0;k<cnt;k++)//枚舉i-2行的排列情況
				{
					if(str[i]&str[k]) continue;
					if(dp[r-1][k][j]==-1) continue;
					dp[r][j][i]=max(dp[r][j][i],dp[r-1][k][j]+sum[i]);//更新即可
				}
			}
		}
	}
	int ans=0;//統計答案
	for(int i=0;i<cnt;i++)
	{
		for(int j=0;j<cnt;j++)
		{
			ans=max(ans,dp[n-1][i][j]);
		}
	}
	printf("%d\n",ans);
	return 0;
}

    小結:這道題非常經典,我們有一種用空間換時間的辦法就是4維dp。

      錯誤:臥槽!!一定牢記...判斷時用的是運算符&而不是&&!!!

[poj1185]炮兵陣地_狀壓dp