POJ 1185 炮兵阵地

典型的状态压缩动态规划,用位来表示各个状态可以显著加快运算速度、节省存储空间。

对于一个位置,如果它部署了一支部队,那么会对它的前后左右的2格位置产生影响,如果以行作为状态,则不能单单由上一状态转移,那样的话不能保证在它的2格处不发生矛盾,而在这个题目中,以行做状态又很明显,所以在状态的转移时必须是上一行的状态和上上行的状态一起转移,如果用f[i,j,k]来表示在第i行时,取第j个状态(注意是第j个状态),i-1行取的是第k个状态,那么不难得出状态转移方程:


     f[i,j,k]=max{f[i,j,k],f[i-1,k,l]+ff[i,j]}


    解释一下这个方程i,j,k的含义如上述,l表示在第i-2行取第l个状态,ff[i,j]表示在第i行取第j个状态时在该行放置的多少支部队,由于第二行仅与第一行有关,所以要对第二行进行一下特殊处理,对于第二行,我们有


     f[2,j,k]=max{ff[2,j]+ff[1,k]};


     这样一来,对动态规划的模型基本处理就完成了,同时我们发现,在m=10时,每一行的状态有2^10种,显然时间和空间都不能承受,必须做出优化,仔细阅读题目,发现如果一个点部署了部队,则其前后两格范围内均不能再部署部队,而且在一个格子为H时,它根本不能部署部队,因此,在许多的状态中,有许多状态是自相矛盾的,根本不需要考虑,可以用某种预处理,把状态提取出来,仅仅储存有用的状态,在这里我的方法是用深度优先搜索预处理一下,try(x:longint)为处理某一行的第x个位置,如果这个位置部署部队,那么x+1,x+2这两个位置都不能放,直接try(x+3)即可,而如果这个地方是山地,就不能放,则try(x+1),即使是平原,也可以不去部署部队,因此还原后也要try(x+1),预处理结束。


     对于状态的储存,为了简洁,用一个二进制数来表示,如当m=4时有一种状态为放,不放,不放,放,就用1代表放,0代表不放,这个二进制数为1001,转化为十进制数为9.这样状态的储存问题就迎刃而解了。


     接下来,判断状态的矛盾问题,这用到了位运算的知识,1001and0011=0001 这已经很明了了,判断两行间的状态是否矛盾,只需要用(状态1)and(状态2),如果结果是0,说明两个状态不矛盾。
 参考 http://www.cnblogs.com/neverforget/archive/2011/10/13/connon.html

#include <vector>
#include <list>
#include <limits.h>
#include <map>
#include <set>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <string.h>
#include <stdlib.h>
using namespace std;

vector<int> pt, num;
int row, col;
int dp[101][62][62];
void get_pt(int c, int cur);
int main(){
    string land[105];
    int maze[105];
    cin>>row>>col;
    pt.clear(); num.clear();
    for(int i=0; i<row; i++) cin>>land[i];
    memset(maze, 0, sizeof(maze));
    for(int i=0; i<row; i++){
        for(int j=0; j<col; j++){
            if(land[i][j]=='H')
                maze[i] = maze[i]|(1<<(col-1-j));
        }
    }
    get_pt(0, 0);
    //cout<<"size: "<<pt.size()<<endl;
    int len = pt.size();
    memset(dp, 0, sizeof(dp));
    int res = 0;
    // for a special case
    if(row==1){
        for(int i=0; i<len; i++){
            if((pt[i]&maze[0])==0)
                res += num[i];
        }
        cout<<res<<endl;
        return 0;
    }
    // for first two line case
    for(int i=0; i<len; i++){
        if((pt[i]&maze[1])!=0) continue;
        for(int j=0; j<len; j++){
            if((pt[j]&maze[0])!=0) continue;
            if((pt[i]&pt[j])!=0) continue;
            dp[1][i][j] = num[i]+num[j];
        }
    }// end external for loop

    // use dynamic programming for remaining rows
    for(int i=2; i<row; i++){
        // iterate over current row
        for(int j=0; j<len; j++){
            if((pt[j]&maze[i])!=0) continue;
            // iterate over last row
            for(int k=0; k<len; k++){
                if((pt[k]&maze[i-1])!=0) continue;
                if((pt[j]&pt[k])!=0) continue;
                // iterate over pre-pre row
                for(int p=0; p<len; p++){
                    if((pt[p]&pt[j])!=0) continue;
                    if((pt[p]&maze[i-2])!=0) continue;
                    dp[i][j][k] = max(dp[i][j][k], dp[i-1][k][p]+num[j]);
                }
            }
        }
    }

    for(int i=0; i<len; i++){
        for(int j=0; j<len; j++)
            res = max(res, dp[row-1][i][j]);
    }
    cout<<res<<endl;
	return 0;
}

void get_pt(int c, int cur){
    if(c >= col){
        cur >>= (c-(col-1));
        pt.push_back(cur);
        num.push_back(__builtin_popcount(cur));
        return;
    }
    // do nothing
    get_pt(c+1, cur<<1);
    // place
    get_pt(c+3, (cur|1)<<3);
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值