您的位置:首页 > 其它

POJ 1185 炮兵阵地(状态dp)

2016-07-05 11:05 309 查看
这个题目和上一个种玉米的是一个类型,都是状态dp,用二进制位来表示当前的一个状态值,只不过比上一个稍微复杂了一点,需要用三维的数组来保存当前state。

题目:在一个N*M的矩阵上布置炮兵部队,只有平原可以布置,然后每个炮兵部队都有一个攻击范围,它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。

问:如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队?

考虑到每一行的状态只和前两行有关,考虑用dp来做。由于是求最多能放置的炮兵个数,就是求某一个状态下,它对应的炮兵个数最多,所以就想到dp方程肯定是那种dp[i]=max{dp[i-1]..}的形式。

我们用1来表示平原地形P,这样对于原始的矩阵,可以每一行的地形将对应一个01表示的地形状态,存到rState
中,用来检查该行炮兵的此种安排状态是否合法。

由于当前行和前两行有关系,所以得用3维矩阵来保存一个状态下最多的炮兵个数,用dp[i][curSt][preSt]表示当前第i行状态对curSt,前一行状态为preSt的最大炮兵数。

转移方程为dp[i][curSt][preSt]=max{dp[i-1][preSt][prepreSt]+curSt状态炮兵安排数量},这样求到最后一行之后,答案就是最后一行所有状态中最大的那个。

程序初始化的时候需要对第一行进行预处理,设置dp[0][st][0]=合法的st中1的个数。这样进行下面的计算的时候,由于0状态肯定是和所有状态兼容的,所以就不会影响计算结果。

代码如下:

#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<string>

const int MaxState = 61;
const int MaxCol = 12;
const int MaxRow = 105;
int dp[MaxRow][MaxState][MaxState];//dp[i][j][k]第i行当前状态为j,i-1行状态为k时能放置的最大炮兵数
int ValidSt[MaxState];//不考虑地形条件下满足条件的炮兵放置方式(状态)
int PaoNum[MaxState];//每种状态放置的炮兵数量
int rState[MaxRow];//地形条件二进制表示数组

int n, m, valid_num;

void initDp() {
valid_num = 0;
for (int i = 0; i < (1 << m); ++i) {
if (!(i&(i << 1)) && !(i&(i << 2))) {
ValidSt[valid_num] = i;
int temp_i = i;
int one_num = 0;
while (temp_i) {
temp_i &= temp_i - 1;
one_num++;
}
PaoNum[valid_num++] = one_num;//不考虑地形条件下状态i可安装炮兵数量
}
}
for (int i = 0; i < valid_num; ++i) {
if (!((~rState[0]) & ValidSt[i])) {
dp[0][i][0] = PaoNum[i];
}
}
}

int main() {
//while (std::cin >> n >> m, n) {

std::cin >> n >> m;
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
char temp;
std::cin >> temp;
if (temp == 'P') {
rState[i] |= (1 << (m - j - 1));
}
}
}
memset(dp, -1, sizeof(dp));

initDp();//在不考虑地形条件下枚举所有满足不相邻条件的情况,并初始化dp[0][满足条件的状态][0]

for (int i = 1; i < n; ++i) {
for (int j = 0; j < valid_num; ++j) {// 第i行的第j中状态
if (!((~rState[i]) & ValidSt[j])) {//第i行炮兵安排状态j与地形是否符合
for (int k = 0; k < valid_num; ++k) {//遍历i-1行所有状态
if (!((~rState[i - 1]) & ValidSt[k]) && !(ValidSt[j] & ValidSt[k])) {//考虑地形条件和相邻上下行不可同时放炮兵,1表示在此位置放置炮兵
for (int t = 0; t < valid_num; ++t) {//遍历i-2行所有状态
if (!(~rState[i - 2] & ValidSt[t]) && !(ValidSt[j] & ValidSt[t])) {//!(ValidSt[j] & ValidSt[t])表示上下两行相同位置不能同时为1
dp[i][j][k] = std::max(dp[i][j][k], dp[i - 1][k][t] + PaoNum[j]);
}
}
}
}
}
}
}

int count = 0;
for (int i = 0; i < valid_num; ++i) {
for (int j = 0; j < valid_num; ++j) {
count = std::max(count, dp[n - 1][i][j]);
}
}
std::cout << count << std::endl;

/*}*/
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: