POJ 1185 炮兵阵地

来源:互联网 发布:软件著作权 用户手册 编辑:程序博客网 时间:2024/06/05 18:19

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

对于一个位置,如果它部署了一支部队,那么会对它的前后左右的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
原创粉丝点击