POJ 1185-炮兵阵地(状压DP+预处理)

题目链接:POJ 炮兵阵地
Description
司令部的将军们打算在NM的网格地图上部署他们的炮兵部队。一个NM的地图由N行M列组成,地图的每一格可能是山地(用"H" 表示),也可能是平原(用"P"表示),如下图。在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队);一支炮兵部队在地图上的攻击范围如图中黑色区域所示:
在这里插入图片描述
如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。图上其它白色网格均攻击不到。从图上可见炮兵的攻击范围不受地形的影响。
现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队。

Input
第一行包含两个由空格分割开的正整数,分别表示N和M;
接下来的N行,每一行含有连续的M个字符(‘P’或者’H’),中间没有空格。按顺序表示地图中每一行的数据。N <= 100;M <= 10。

Output
仅一行,包含一个整数K,表示最多能摆放的炮兵部队的数量。
Sample Input
5 4
PHPP
PPHH
PPPP
PHPP
PHHP
Sample Output
6

思路:
题目是求最优解的问题,首先看数据,最大是100 * 10的地图,若是搜索的话对于每个‘P’都有放和不放两种情况,肯定爆,因此考虑dp。

描述dp状态
一行一行考虑放置情况进行递推,所以行数作为一个参数。但“前 i 行能摆放的方法数”这个状态值并不能由其他状态推出,所以还需要加维度,发现一门炮的射程是2,那当前位置能否摆一门炮,取决于前两行这个位置的放置情况,因此最新两行的摆放情况也要作为描述状态的参数,刚好地图的宽度<=10,可以把一行的摆放状态状压成一个int,1代表放置,0代表不放。dp[i][j][k]表示第i行的状态是j,上一行状态是k的摆法最大种数。
但是,状压后表示一行摆放情况的整数范围大概是[0, 2^10],开设dp[100][1024][1024]的数组太大,所以要预处理,因为同一行并不可能摆满炮,它们至少间隔为2,这样一行的摆法会少很多。

dfs预处理

void dfs(int x, int pos){//	 x表示一行中的第几个位置,pos是状压的摆放情况
	if( x > M-1){	//	M是地图列数
		states[depth++] = pos;
		return ;
	}
	dfs( x + 1, pos);	//	当前位置不放
	pos |= ( 1 << x);
	dfs( x + 3, pos) ;	//	当前位置放
}

调用dfs(0, 0)可以预处理出所有摆法存起来,states[i]表示第i种摆法。
假设地图宽10,且全部是平地,算出来最多有60种摆法,所以开设dp[110][70][70]即可。

递推之前,先对前两行初始化:(num(j)表示第j种摆法的炮数)
dp[0][j][0] = num(j)
dp[1][i][j] = max{dp[0][j][0]} + num(i)
状态转移方程:
dp[i][j][k] = max{dp[i-1][k][m], m = 0…depth} + num(j),

代码:

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

int N, M;
int states[70];
int depth = 0;
int dp[110][70][70];
char area[110][20];

void dfs(int x, int pos){//	 x表示一行中的第几个位置,pos是状压的摆放情况
	if( x > M-1){	//	M是地图列数
		states[depth++] = pos;
		return ;
	}
	dfs( x + 1, pos);	//	当前位置不放
	pos |= ( 1 << x);
	dfs( x + 3, pos) ;	//	当前位置放
}

int main(){
	cin >> N >> M;
	dfs(0, 0);
	for( int i = 0; i < N; ++i)
		for( int j = 0; j < M; ++j)
			cin >> area[i][j];
	for( int i = 0; i < depth; ++i){//	初始化第一层 
		int cnt = 0;
		for( int j = 0; j < M; ++j){
			if( (states[i] & (1 << j)) != 0 && area[0][j] == 'H'){
				cnt = 0;
				break;
			}
			if( (states[i] & (1 << j)) != 0)
				cnt++;
		}
		dp[0][i][0] = cnt;
	}
	for( int i = 0; i < depth; ++i){//	初始化第二层 
		int nowmax = 0;
		for( int k = 0; k < depth; ++k){
			if( (states[i] & states[k]) != 0){//		ik不相容 
				dp[1][i][k] = 0;
				continue;
			}
			bool illegal = false;
			for( int j = 0; j < M; ++j){//	ik相容则判断地形 
				if( (states[i] & (1 << j)) != 0 && area[1][j] == 'H'){//	不合法,与地形冲突 
					dp[1][i][k] = 0;
					illegal = true;
					break;
				}
			}
			if( illegal == false){//		表明ik相容且与地形也不冲突
				int cnt = 0;
				for( int j = 0; j < M; ++j)
					if( (states[i] & (1 << j)) != 0)
						cnt++;
				dp[1][i][k] = dp[0][k][0] + cnt;
			}
		}
	}
	for( int i = 2; i < N; ++i){	//	开始递推每一层 dp[i][j][k]
		for( int j = 0; j < depth; ++j){
			bool illegal = false;
			for( int p = 0; p < M; ++p){//	判断地形 
				if( (states[j] & (1 << p)) != 0 && area[i][p] == 'H'){ //不合法,与地形冲突 
					illegal = true;
					break;
				}
			}
			if( illegal == true)
				continue;
			//若地形合法,看jk 			
			for( int k = 0; k < depth; ++k){
				if( (states[j] & states[k]) != 0){//		jk不相容 
					dp[i][j][k] = 0;
					continue;
				}
				int nowmax = 0;
				for( int m = 0; m < depth; ++m){
					if( (states[j] & states[m]) == 0)//	第i行必须要和第i-2行不冲突 
						nowmax = max( nowmax, dp[i-1][k][m]);	
				}
				int cnt = 0;
				for( int p = 0; p < M; ++p)
					if( (states[j] & (1 << p)) != 0)
						cnt++;
				dp[i][j][k] = nowmax + cnt;
			}
		}
	}
	int themax = 0;
	for( int i = 0; i < N; ++i){
		for( int j = 0; j < depth; ++j){
			for( int k = 0; k < depth; ++k)
				themax = max( themax, dp[i][j][k]);
		}
	}
	cout << themax << endl;
	
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值