POJ 1185 炮兵阵地 状压dp

2016-10-26 来源: fenicnn 发布在  https://www.cnblogs.com/fenice/p/6002417.html

题目链接:

http://poj.org/problem?id=1185

炮兵阵地

Time Limit: 2000MS
Memory Limit: 65536K

问题描述

司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队。一个N*M的地图由N行M列组成,地图的每一格可能是山地(用"H" 表示),也可能是平原(用"P"表示),如下图。在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队);一支炮兵部队在地图上的攻击范围如图中黑色区域所示:
如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。图上其它白色网格均攻击不到。从图上可见炮兵的攻击范围不受地形的影响。
现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队。

输入

第一行包含两个由空格分割开的正整数,分别表示N和M;
接下来的N行,每一行含有连续的M个字符('P'或者'H'),中间没有空格。按顺序表示地图中每一行的数据。N <= 100;M <= 10。

输出

仅一行,包含一个整数K,表示最多能摆放的炮兵部队的数量。

样例输入

5 4
PHPP
PPHH
PPPP
PHPP
PHHP

样例输出

6

题解

第一感觉就是转移和前两行的状态有关,这样就变成2^20了,转移的时候会直接炸掉。

正解是,只记录有效的状态,这样发现一行有效的状态就60个,两行的也就3600,然后就可以直接按行转移了。

代码

#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<stack>
#include<ctime>
#include<vector>
#include<cstdio>
#include<string>
#include<bitset>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<functional>
using namespace std;
#define X first
#define Y second
#define mkp make_pair
#define lson (o<<1)
#define rson ((o<<1)|1)
#define mid (l+(r-l)/2)
#define sz() size()
#define pb(v) push_back(v)
#define all(o) (o).begin(),(o).end()
#define clr(a,v) memset(a,v,sizeof(a))
#define bug(a) cout<<#a<<" = "<<a<<endl
#define rep(i,a,b) for(int i=a;i<(b);i++)
#define scf scanf
#define prf printf

typedef long long LL;
typedef vector<int> VI;
typedef pair<int,int> PII;
typedef vector<pair<int,int> > VPII;

const int INF=0x3f3f3f3f;
const LL INFL=10000000000000000LL;
const double eps=1e-9;

const double PI = acos(-1.0);

//start----------------------------------------------------------------------

const int maxn=111;
const int maxs=66;
int n,m;

///存有效状态
VI mysta;
vector<LL> sumv;

///dp[cur][i][j]现在考虑前cur行,前1行状态是i,第cur行状态是j,能塞下的最多炮兵。
LL dp[maxn][maxs][maxs];

///预处理出所有未冲突的状态
void pre() {
    for(int i=0; i<(1<<10); i++) {
        int cnt=0;
        int mi=INF,pre=-100;
        for(int j=0; j<10; j++) {
            if(i&(1<<j)) {
                cnt++;
                if(mi>j-pre) mi=j-pre;
                pre=j;
            }
        }
        if(mi<=2) continue;
        mysta.pb(i);
        sumv.pb(cnt);
    }
}

char str[maxn][22];
int tot;

///判断有没有吧炮搭到山上
bool ok(int x,int i) {
    for(int j=0; j<m; j++) {
        if(!(x&(1<<j))) continue;
        if(str[i][j]=='H') {
            return false;
        }
    }
    return true;
}

///判断上方冲突
bool ok2(int pre,int cur) {
    for(int i=0; i<m; i++) {
        if((cur&(1<<i))==0) continue;
        if(pre&(1<<i)) return false;
    }
    return true;
}

///特判只有一行的情况
void solve1() {
    LL ans=0;
    for(int i=0; i<tot; i++) {
        if(ok(mysta[i],0)) ans=max(ans,sumv[i]);
    }
    prf("%lld\n",ans);
}

int main() {
    pre();
    while(scf("%d%d",&n,&m)==2&&n) {
        tot=upper_bound(all(mysta),(1<<m)-1)-mysta.begin();
        rep(i,0,n) scf("%s",str[i]);
        if(n==1) {
            solve1();
            continue;
        }

        ///预处理出前两行
        clr(dp,0);
        for(int i=0; i<tot; i++) {
            if(!ok(mysta[i],0)) continue;
            for(int j=0; j<tot; j++) {
                if(!ok(mysta[j],1)) continue;
                if(!ok2(mysta[i],mysta[j])) continue;
                dp[1][i][j]=sumv[i]+sumv[j];
            }
        }

        ///递推
        for(int cur=2; cur<n; cur++) {
            for(int k=0; k<tot; k++) {
                if(!ok(mysta[k],cur)) continue;
                for(int i=0; i<tot; i++) {
                    if(!ok2(mysta[i],mysta[k])) continue;
                    for(int j=0; j<tot; j++) {
                        if(!ok2(mysta[j],mysta[k])) continue;
                        dp[cur][j][k]=max(dp[cur][j][k],dp[cur-1][i][j]+sumv[k]);
                    }
                }
            }
        }

        LL ans=0;
        for(int i=0; i<tot; i++) {
            for(int j=0; j<tot; j++) {
                ans=max(ans,dp[n-1][i][j]);
            }
        }

        prf("%lld\n",ans);
    }
    return 0;
}

//end-----------------------------------------------------------------------

Notes

当上帝关上一扇门的同时,会给你开启新的一扇门。
这题相比方格取数虽然状态变成两行了,但是有效的状态却变少了。

相关文章