POJ 1185 - 炮兵阵地

28 篇文章 0 订阅

Description

司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队。一个N*M的地图由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



把题意简化一下就是地形对放置炮兵有影响,上一行和上两行对放置炮兵也有影响,要在不受影响的情况下找出放置最多的炮兵数。

这是一道状态压缩的DP问题,至于怎么看出是状态压缩,那就是数据量了。100行10列,如果使用普通的DP还要在考虑上两行的情况,那么肯定超时。所以使用了状态压缩。

base用来存储压缩后的地图,state用来存放不考虑地形的情况下【即只有炮兵之间不能相互攻击到】压缩后的情况,soldier用来存放第num种情况的时候能放多少种炮兵,在这里要注意,state和soldier都是对于每一行来说的,还暂时没有考虑前一行和前两行。

然后初始化第一行和第二行【原因你懂得】。

最后按照状态转移方程进行DP就行了,具体代码看下面:

#include <cstdio>
#include <cstring>

int row, col;
int ans[110][70][70];//ans[i][j][k] stands for i-th is state[j], i-1-th is state[k];
char maps[110][20];
int base[110];//every row of maps in number;
int state[70], soldier[70];//state for situation, soldier for num of soldiers;
int num;

inline int maxn(int a, int b)
{
    return a > b ? a : b;
}

int main()
{
    memset(ans, 0, sizeof(ans));
    memset(base, 0, sizeof(base));
    memset(state, 0, sizeof(state));
    memset(soldier, 0, sizeof(soldier));
    num = 0;//record num of accepted situation that only 2 soldiers cant attk;

    scanf("%d%d", &row, &col);//read data
    for (int i = 0; i < row; ++i)
    {
        scanf("%s", maps[i]);
        for (int j = 0; j < col; ++j)
        {
            if (maps[i][j] == 'H')
                base[i] += (1<<j);
        }
    }

    for (int i = 0; i < (1<<col); ++i)
    {
        if ((i & (i<<1)) || (i & (i<<2))) continue;//there r 2 soldiers can attk each other
        int sym = i;
        while (sym)//record num of soldiers
        {
            soldier[num] += sym&1;
            sym = sym >> 1;
        }
        state[num++] = i;//record situation
    }

    for (int i = 0; i < num; ++i)//initialize ans[0][i][0]
    {
        if (base[0] & state[i]) continue;//soldier cant put on mountain
        ans[0][i][0] = soldier[i];
    }
    for (int i = 0; i < num; ++i)//initialize ans[1][i][j]
    {
        if (base[1] & state[i]) continue;
        for (int j = 0; j < num; ++j)
        {
            if (base[0] & state[j]) continue;
            if (state[i] & state[j]) continue;
            ans[1][i][j] = maxn(ans[1][i][j], ans[0][j][0] + soldier[i]);
        }
    }

    for (int r = 2; r < row; ++r)//i-th row
    {
        for (int i = 0; i < num; ++i)//i-th row situation
        {
            if (base[r] & state[i]) continue;
            for (int j = 0; j < num; ++j)//(i-1)-th situation
            {
                if (base[r-1] & state[j]) continue;
                if (state[j] & state[i]) continue;
                for (int k = 0; k < num; ++k)//(i-2)-th situation
                {
                    if (base[r-2] & state[k]) continue;
                    if (state[j] & state[k]) continue;
                    if (state[i] & state[k]) continue;
                    ans[r][i][j] = maxn(ans[r][i][j], ans[r-1][j][k] + soldier[i]);
                }
            }
        }
    }
    int ret = 0;
    for (int i = 0; i < num; ++i)
        for (int j = 0; j < num; ++j)
            ret = maxn(ret, ans[row-1][i][j]);
    printf("%d\n", ret);
    return 0;
}

PS.一开始自己写就是不过,后来借鉴了一位大神的代码才知道自己存状态的情况考虑的优点不对。

大神在这→戳这里

PSS.POJ的中文题,好幸福TUT

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值