【poj 1185】【codevs 1647】炮兵阵地 状压动规

这个题目,嗯。我在codevs成功AC,但是poj上面WA,我不想研究为什么了。。。于是直接发题解希望各位看我博客的大犇对我不完美的代码提出宝贵的意见,谢谢。
POJ

CODEVS

这里写图片描述
这里写图片描述
这个是两个OJ的评测状态。

炮兵阵地
Time Limit: 2000MS Memory Limit: 65536K
Total Submissions: 26349 Accepted: 10158
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

题解:
嗯,还是选择用状压动规,N <= 100;M <= 10。所以说我选择从上往下排炮兵,这样每一行的状态为2^10个为1024似乎好可以忍受,但是我怎么在能打两行的情况下依然让我的炮兵可以排列呢?似乎将种玉米的数组改动一下就可以了。

定义dp[i][j][k] 第i行排列方式为j,第i-1行的排列方式为k,现在前i行最多排列的炮兵的数量

但是现在一个新问题出现呢,dp[100][1024][1024]空间无法忍受。似乎时间也过不去?

嗯,然后就用状压动规中经常用的一个优化——把可行的状态预处理出来,因为我每一行最多10格,而每个炮兵的攻击距离为2,所以我每一行最多放4个炮兵所以说我的状态不足70种。开个新数组state[j]用来表示第j种状态的二进制,这样的话dp[100][70][70];就可以了。

剩下的就先对都是平地的情况dfs出所有的可行状态,对每一个状态枚举前面的状态转移到这个状态的最大炮兵数量。

PS:有一个点需要说明的就是,别人的代码都有将dp数组赋值为-1,然后对-1的不转移到后面,我认为因为你前面的状态-1和0没有区别因为都不会对后面的状态产生影响,所以说我就直接没管,都是0.不知道是不是这儿让我的代码,出错。

代码:

#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>

using namespace std;

int N,M,Top,ans;
int state[700],dp[200][700][700],P[1050],num[700];

bool H[105];

char s[105][1000];

void dfs(int x)
{
    Top++;

    for(int i = 0;i < M;i++)
        if(H[i])
            state[Top] += 1 << i,
                num[Top]++;

    if( x > M - 1 )  return;

    for(int i = x;i < M;i++){

        if(i == 0){
            H[i] = true;
            dfs(i+1);
            H[i] = false;
        }

        else if(i == 1 && !H[i-1]){
            H[i] = true;
            dfs(i+1);
            H[i] = false;
        }

        else if(!H[i-1] && !H[i-2]){
        H[i] = true;
        dfs(i + 1);
        H[i] = false;
        }

    }

}

inline void init()
{
    scanf("%d %d",&N,&M);

    for(int i = 1;i <= N;i++)
        scanf("%s",s[i]);

    for(int i = 1;i <= N;i++)
        for(int j = 0;j < M;j++){
            if(s[i][j] == 'H')P[i] += 1 << j;
        }


}

inline bool check(int x,int y){
    if(x & P[y]) return false;
    return true;
}

int main()
{
    init();

    dfs(0);

    for(int i = 1;i <= Top;i++)        //第一行边界情况
        if(check(state[i],1))dp[1][i][0] = num[i];

    for(int i = 1;i <= Top;i++){       //第二行边界情况
        if(check(state[i],2))
          for(int j = 1;j <= Top;j++)
            if(!(state[i] & state[j])) dp[2][i][j]
            = max(dp[2][i][j],dp[1][j][0] + num[i]);
    }

    for(int i = 3;i <= N;i++)          //对后面的枚举
        for(int j = 1;j <= Top;j++)
        if(check(state[j],i))
            for(int k = 1;k <= Top;k++)
                if(!(state[j] & state[k]))  
                    for(int f = 1;f <= Top;f++)
                        if(!(state[j] & state[f]))
                            dp[i][j][k] =
                                max(dp[i][j][k],dp[i-1][k][f] + num[j]);

    for(int i = 1;i <= Top;i++)
        for(int j = 1;j <= Top;j++)
            ans = max(ans,dp[N][i][j]);

    printf("%d\n",ans);

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值