zoukankan      html  css  js  c++  java
  • POJ1185炮兵阵地(状态压缩DP)

    POJ飞翔.数据弱

    ZQOJ飞翔 数据强

    Description

    司令部的将军们打算在N×M的网格地图上部署他们的炮兵部队。一个N×M的地图由N行M列组成,地图的每一格可能是山地(用"H" 表示),也可能是平原(用"P"表示),如下图。在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队);一支炮兵部队在地图上的攻击范围如图中黑色区域所示:

    如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。图上其它白色网格均攻击不到。从图上可见炮兵的攻击范围不受地形的影响。现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队。

    Input

    多测试数据。

    每组测试数据的第一行包含两个由空格分隔的正整数,分别表示N和M;  0 ≤ N ≤ 100;0 ≤ M ≤ 10。

    接下来的N行,每一行含有连续的M个字符('P'或者'H'),中间没有空格。按顺序表示地图中每一行的数据。

    Output

    每组测试数据输出一行,包含一个整数K,表示最多能摆放的炮兵部队的数量。

    Sample Input

    5 4
    PHPP
    PPHH
    PPPP
    PHPP
    PHHP

    Sample Output

    6

    题目分析:

    经典NOI题,矩阵里的状态压缩问题。因为m<=10,而每列都有状态选或不选,所以想到用2进制,那么状态数是2^10。因为当前行的选择依赖于前两行,而前一行又依赖于前前两行,能想到状态转移方程应该牵扯到当前行、前一行、前前行,类似于递推式dp[i] = dp[i-1] + dp[i-2]的递推过程,而本体每次都是状态间的转移,想到状态转移方程dp[i][j][k] = max(dp[i][k][l]) + sum[j](j和k和l表示当前行状态,前一行状态,前前行状态,sum[j]表示j状态下在i行放了几个大炮)。

    用上面的转移方程,空间复杂度和时间复杂度都不允许,因为j,k,l<=2^10,而实际的情况是10列的组合中不冲突的组合只有少数几种,比如PHPP,状态5(101)表示的在第0列和第2列放炮,这个状态内部冲突,我们就可以不考虑,可以预处理把这些状态剔除,然后将不冲突的状态存进一个数组,转移的时候用数组的下标去转移就好。状态转移方程变成:dp[i][j][k] = max(dp[i][k][l]) + one[i][j](j,k,l分别表示第i行,第i-1行,第i-2行的第j个、第k个,第l个状态,状态分别为state[i][j],state[i-1][k],state[i-2][l],one[i][j]表示第i行状态j的1的个数,也就是i状态下放炮数量),最坏复杂度O(N*K^3)(K<62)

    AC代码:

    #include<stdio.h>
    #include<string.h>
    #include<algorithm>
    using namespace std;
    const int MAX = 110;
    const int INF = 0x3f3f3f3f;
    const int maxn = 70;
    ///state[i][j]表示第i行第j个合法状态,one表示第i行第j个合法状态中含1的个数
    int state[MAX][MAX],one[MAX][MAX],ans;
    ///stnum[i]表示i行合法的状态数,sum[i]为i状态下1的个数
    int stsum[MAX],sum[MAX*20];
    ///dp[i][j][k]表示第i行第j个状态第-1行第k个状态含有的最多1的个数
    int dp[MAX][maxn][maxn],map[MAX][MAX],n,m;
    void init( )
    {
        ans=0;
        memset(dp,0,sizeof(dp));
        memset(map,0,sizeof(map));
        memset(one,0,sizeof(one));
        memset(stsum,0,sizeof(stsum));
    }
    //每个状态里面含有1的数量算出来
    void onesum( )
    {
        for(int i=0 ; i<=(1<<10) ; i++)
        {
            int t=0;
            for(int j=0 ; j<=10 ; ++j)
            if(i&(1<<j))
            t++;
            sum[i]=t;
        }
    }
    ///判断状态是否符合
    bool ok(int x)
    {
        if(x>1&&(x&(x>>1)))
        return 0;
        if(x>2&&(x&(x>>2)))
        return 0;
        return 1;
    }
    ///把第x行中合法的状态全部找出来,存到state数组中,tot是本行所有的p点压缩起来的一个状态
    void STATE(int x,int tot)
    {
        for(int i=0 ; i<(1<<m) ; i++)
        if(ok(i)&&(i&tot)==i)
        ///(i&tot) == i表示集合i是集合tot的子集合,意思是i里面的含有的列都是p点
        {
            stsum[x]++;
            int t=stsum[x];
            state[x][t]=i;
            one[x][t]=sum[i];
        }
    }
    int main( )
    {
        onesum( );
        char tp[MAX];
        while(scanf("%d%d",&n,&m)!=EOF)
        {
            init( );
            stsum[0]=maxn;
            for(int i=1 ; i<=n ; i++)
            {
                scanf("%s",tp);
                int k=0;
                for(int j=0 ; j<m ; j++)
                {   //将图变为0,1图
                     map[i][j] = (tp[j] == 'P' ? 1 : 0);
                    k += (map[i][j] ? (1 << j) : 0);
                }
                STATE(i,k);
    
            }
            ///第一行
            for(int i=1 ; i<=stsum[1] ; i++)
            for(int j=1 ; j<=stsum[0] ; j++)
            dp[1][i][j]=one[1][i];
            for(int i=2 ; i<=n ; i++)
            for(int j=1 ; j<=stsum[i] ; j++)///枚举第i行的状态
            for(int k=1 ; k<=stsum[i-1] ; k++)///枚举第i-1行的状态
            {/// 判断两个状态是否有冲突
                if(state[i][j]&state[i-1][k])
                continue;
                int t=0;
                for(int s=0 ; s<=stsum[i-2] ; s++)///枚举第i-2行的状态
                {
                    if(state[i][j]&state[i-2][s])/// 判断三个状态是否有冲突
                    continue;
                    if(state[i-1][k]&state[i-2][s])
                    continue;
                    t=max(dp[i-1][k][s],t);
                }
                dp[i][j][k]=t+one[i][j];
            }
            for(int j=1 ; j<=stsum[n] ; j++)
            for(int k=1 ; k<=stsum[n-1] ; k++)
            ans=max(ans,dp[n][j][k]);
            printf("%d
    ",ans);
        }
        return 0;
    }
    View Code
  • 相关阅读:
    安卓android.support.design使用中的问题
    处理requests SSl 证书问题
    python-excel
    post 请求包含相同参数
    关于zk 页面滚动问题 scroll
    Usefull Jquery
    Git 安装
    Comparison issues in TD
    Work Diary 12/13/17
    Unit10 I don't like work in the weekend
  • 原文地址:https://www.cnblogs.com/shuaihui520/p/9143237.html
Copyright © 2011-2022 走看看