POJ 1185 NOI 2001 炮兵阵地 状压DP

题目大意:给出一个地图,有两种点,P点可以站人,H点不能站人。每放一个人就会对他上下左右各两个格子产生影响,产生影响的格子不能放人。问最多能放多少个人。


思路:数据范围指引解题的方向。题中给出M<=10,这是一个很小的数字,2^10也不过才1024,用这个来dp就轻松多了。于是我们先预处理出每一行可能出现的状态,要注意一行中不能有两个距离<2。大表之后发现,每一行最多只能有60个左右。现在可以放心做O(n^3×m)的dp了。处理上下几行的关系的时候要注意status&status_==0时才能转移。

最后是如果m<=2的时候要特判,直接从状态中找一个最大值输出就可以。


CODE:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

int m,n;
char src[110][110];
int status[110][1500];
int num[1500];

int f[110][110][110];

void Pretreatment()
{
	for(int i = 1; i <= (1 << n); ++i)
		num[i] = num[i >> 1] + (i&1);
}

inline bool Judge(int x,int status)
{
	static bool v[110];
	memset(v,false,sizeof(v));
	int p = 0;
	while(status) {
		v[p++] = status&1;
		status >>= 1;
	}
	for(int i = 0; i < n; ++i)
		if(v[i]) {
			if(src[x][i] == 'H')	return false;
			if(v[i - 1] || v[i - 2])	return false;	
		}
	return true;
}

int main()
{
	cin >> m >> n;
	Pretreatment();
	for(int i = 1; i <= m; ++i)
		scanf("%s",src[i]);
	for(int i = 1; i <= m; ++i)
		for(int j = 0; j < (1 << n); ++j)
			if(Judge(i,j))
				status[i][++status[i][0]] = j;
	for(int j = 1; j <= status[2][0]; ++j)
		for(int i = 1; i <= status[1][0]; ++i)
			if((status[1][i]&status[2][j]) == 0)
				f[2][j][i] = max(f[2][j][i],num[status[1][i]] + num[status[2][j]]);
	if(m == 1 || m == 2) {
		int ans = 0;
		for(int i = 1; i <= status[1][0]; ++i)
			ans = max(ans,num[status[1][i]]);
		for(int i = 1; i <= status[2][0]; ++i)
			ans = max(ans,num[status[2][i]]);
		cout << ans << endl;
		return 0;
	}
	int ans = 0;
	for(int i = 3; i <= m; ++i)
		for(int j = 1; j <= status[i][0]; ++j)
			for(int k = 1; k <= status[i - 1][0]; ++k)
				for(int l = 1; l <= status[i - 2][0]; ++l) {
					if((status[i][j]&status[i - 1][k]) || (status[i - 1][k]&status[i - 2][l]) || (status[i][j]&status[i - 2][l]))	continue;
					f[i][j][k] = max(f[i][j][k],f[i - 1][k][l] + num[status[i][j]]);
					ans = max(ans,f[i][j][k]);
				}
	cout << ans << endl;
	return 0;
}


你可能感兴趣的:(动态规划,poj,1185,状压dp,NOI2001)