您的位置:首页 > 其它

炮兵阵地(状态压缩dp)

2018-03-25 11:26 232 查看

炮兵阵地

时间限制:2000 ms  |  内存限制:65535 KB难度:6描述司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队。一个N*M的地图由N行M列组成,地图的每一格可能是山地(用"H" 表示),也可能是平原(用"P"表示),如下图。在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队);一支炮兵部队在地图上的攻击范围如图中黑色区域所示: 



如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。图上其它白色网格均攻击不到。从图上可见炮兵的攻击范围不受地形的影响。 
现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队。输入第一行输出数据测试组数X(0<X<100)
接下来每组测试数据的第一行包含两个由空格分割开的正整数,分别表示N和M; 接下来的N行,每一行含有连续的M个字符('P'或者'H'),中间没有空格。按顺序表示地图中每一行的数据。0<=N <= 100;0<=M <= 10。输出每组测试数据输出仅一行,包含一个整数K,表示最多能摆放的炮兵部队的数量。
样例输入
1
5 4
PHPP
PPHH
PPPP
PHPP
PHHP
样例输出
6


状态压缩问题(不大懂,学习一下)
思路方法来自点击打开链接

首先分析一下这个题目:
对于某一点(i,j)放置与不放置炮兵问题,相关的点是mp[i][j](平原和山地不同),还有就是i-1的放置情况和i-2的放置情况
而i-1和i-2行的情况影响着下一行放置与不放置的问题和放置多少的问题。
1.状态压缩:
    每一个位置两种状态,即可以用01来表示,即可以表示成一个数字(二进制转化为十进制)常用灵活转化

    每个位置放置与不放置也是两种状态可以用01表示最后转化为一个十进制的数字

2.如何多层for去对应一个个点复杂度太高,而通过状态压缩后的数字来进行对比。(&运算,逐位比较)
    for循环十进制数字转化为二进制,判断:

        (1)左右一位和两位没有炮兵  (2)该地方为平原

3.首先我们单纯的寻找在所有的点都是平原的情况下的可行解
4.再把可行解与地形情况作对比,与运算寻找&值0&1==0 (平原可放), 1 & 0 == 0(高地不可放),0&0 ==0(平原不放),都是可行的。1&1 == 1,不可行
4.第i行的放置状态不可以与i-1行和i-2行冲突
推出对应的递推式子是
dp[i][j][k] = dp[i-1][k][l] + state_num[j]与当前值的最大
i表示第i行,j表示i行的当前状态,k表示i-1行的状态,l表示i-2的当前状态,state_num[j]表示当时j状态所放置的炮兵数

重点,先找状态压缩后的可行解,然后与限制条件对比,找出符合的情况,求递推公式。
  
#include <bits/stdc++.h>
using namespace std;
const int N = 105;
const int M = 11;

int state
;
int state_num
;
int mp
;
int dp

;
int sum_s;

bool cmp(int a, int b)
{
if((a&b) == 0) return true; // 此处注意 == 的优先级高于&运算
else return false;
}

void find_state(int m)//寻找放置炮兵的可行解
{
sum_s = 0;
for(int i = 0; i < (1<<m); i++ )
{
if((i&(i<<1))==0 && (i&(i<<2))==0) //先随左边放
{
//cout << sum_s <<endl;
state[sum_s] = i;
state_num[sum_s] = 0;
for(int j = 1; j <= state[sum_s]; j=(j<<1))
{
if(j&state[sum_s]) state_num[sum_s]++;//判断第i位(倒数)是否为1,也就是求放置炮兵的总数
}
sum_s ++;
}
}
}

int main()
{
int t;
scanf("%d", &t);
while(t--)
{
memset(dp, 0, sizeof(dp));
memset(mp, 0, sizeof(mp));
memset(state, 0, sizeof(state));
memset(state_num, 0, sizeof(state_num));
int n, m;
scanf("%d %d", &n, &m);
find_state(m);
//cout << sum_s <<endl;
if(n == 0)
{
cout << "0" << endl;
continue;
}

char c;
for(int i = 1; i <= n; i++)
{
getchar();
for(int j = 0; j < m; j++)
{
cin >> c;
if(c == 'H') mp[i] += (1<<j);  //这样算出的mp是逆序的,但不影响最终结果
}
//cout << mp[i] << endl;
}

int ans = -1;
for(int i = 0; i < sum_s; i++)
{
//cout << mp[1] << " " << state[i] <<" "<< (mp[1] & state[i]) << endl;
if(cmp(mp[1], state[i]))//合适
{
//cout << "enable" << i << endl;
dp[1][i][0] = max(dp[1][i][0], state_num[i]);
if(n == 1) ans = max(dp[1][i][0], ans);
}
}
if(n >= 2)
{
for(int i = 0; i < sum_s; i++)
{
if(!cmp(mp[2], state[i])) continue;
for(int j = 0; j < sum_s; j++)
{
if(!cmp(state[i], state[j])) continue;
dp[2][i][j] = max(dp[2][i][j], dp[1][j][0]+state_num[i]);
if(n == 2) ans = max(ans, dp[2][i][j]);
}
}
}

if(n > 2)
{
for(int k = 3; k <= n; k++)
{
for(int i = 0; i < sum_s; i++) //第k行
{
if(!cmp(mp[k], state[i])) continue;
for(int j = 0; j < sum_s; j++) //k-1行
{
if(!cmp(state[i], state[j])) continue;
for(int l = 0; l < sum_s; l++)
{
if(!cmp(state[i], state[l])) continue;
if(!cmp(state[j], state[l])) continue;
dp[k][i][j] = max(dp[k][i][j], dp[k-1][j][l] + state_num[i]);
if(k == n) ans = max(dp[k][i][j], ans);
}
}
}
}
}
cout << ans << endl;
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: