zoukankan      html  css  js  c++  java
  • POJ 1185 炮兵阵地 状压dp

    题目链接:

    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
    ",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
    ",ans);
        }
        return 0;
    }
    
    //end-----------------------------------------------------------------------
    

    Notes

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

  • 相关阅读:
    redisLock redis分布式锁
    Mabitis中的#与$符号区别及用法介绍
    pring Scheduler定时器原理分析
    SpringBoot几种定时任务
    线程池的理解
    JVM的方法区和永久带是什么关系
    JVM老年代和新生代的比例
    IO 与 NIO
    对mysql乐观锁、悲观锁、共享锁、排它锁、行锁、表锁概念的理解
    TCP滑动窗口控制流量的原理
  • 原文地址:https://www.cnblogs.com/fenice/p/6002417.html
Copyright © 2011-2022 走看看