状压DP初识~~炮兵阵地

炮兵阵地
Time Limit: 2000MS Memory Limit: 65536K
Total Submissions: 31718 Accepted: 12253

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

题意:中文题面,题意很明显了
我们来考虑一下最暴力的搜索每个点都尝试一遍的话会是1000!
肯定是会爆炸的,所以,既然每行的列数在10以内,那么我们很自然的就想到了状态压缩的dp

将每一行的状态存储为一个二进制数,1表示可以放,0表示不能放
每一层的状态由上一层推导而来,如果上层这个地方放了,那么下面的这个地方就不能放
所以我们要用到二进制的位运算

dp[r][j][i]=max(dp[r1][k][j]+sum[i])

其中sum[i]sum[i]为i状态对应的数量

代码如下:

(参考上海全能王博客

http://cubercsl.cn/solve/2017-summer/emplacement/

#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <vector>
#include <set>
#include <map>
#include <cmath>
#include <queue>
#include <stack>
using namespace std;
const int maxn =105;
const int INF = 0x3f3f3f3f;
const int mod = 1e9+7;
const double eps = 1e-6;
int dp[maxn][maxn][maxn];
int maze[maxn];
vector<int> v,sum;
inline bool ok(int s){
  return !(s&(s<<1))&&!(s&(s<<2));
}
inline int getsum(int s){
  int ret=0;
  for(;s;s>>=1){
    if(s&1) ret++;
  }
  return ret;
}
void init(int m){
  for(int i=0;i <(1<<m);i++){
    if(ok(i)){
      v.push_back(i);
      sum.push_back(getsum(i));
    }
  }
}
int main(){
    int n,m;
    char tmp;
    cin>>n>>m;
    init(m);
    memset(dp,0xc0,sizeof(dp));
    for(int i=0;i<n;i++){
      for(int j=0;j<m;j++){
        cin>>tmp;
        if(tmp=='H') maze[i]|=(1<<j);
      }
    }
    for(int i=0;i<v.size();i++){
      if(!(v[i]&maze[0])){
        dp[0][0][i]=sum[i];
      }
    }
    for(int r=1;r<n;r++)
      for(int i=0;i<v.size();i++)
        if(!(v[i]&maze[r]))
          for(int j=0;j<v.size();j++)
            if(!(v[i]&v[j]))
              for(int k=0;k<v.size();k++)
                if(!(v[i]&v[k]))
                  dp[r][j][i]=max(dp[r][j][i],dp[r-1][k][j]+sum[i]);
    int ans=0;
    for(int i=0;i<v.size();i++){
      for(int j=0;j<v.size();j++){
        ans=max(ans,dp[n-1][i][j]);
      }
    }
    cout<<ans<<endl;
    return 0;
}

 

 

转载于:https://www.cnblogs.com/buerdepepeqi/p/9357189.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值