poj1185 炮兵阵地 经典状态压缩dp

poj1185 炮兵阵地 经典状态压缩dp

状态dp,用二进制位来表示当前的一个状态值,只不过比上一个稍微复杂了一点,需要用三维的数组来保存当前state。

题目:在一个N*M的矩阵上布置炮兵部队,只有平原可以布置,然后每个炮兵部队都有一个攻击范围,它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。

问:如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队?

由于是求的最多能放置的炮兵个数,就是求某一个状态下,它对应的炮兵个数最多,所以就想到dp方程肯定是那种dp[i+1]=max{dp[i-1]..}的形式,又考虑到每一行的状态只和前两行有关系,所以考虑用dp来做,下面考虑如何用二进制位来表示一个状态及转移方程。

由于当前行和前两行有关系,所以得用3维矩阵来保存一个状态下最多的炮兵个数,用dp[i][now][last]表示当前第i行状态,前一行状态的最大炮兵数。

转移方程为dp[i][now][last]=max{dp[i-1][last][llast]},这样求到最后一行之后,答案就是最后一行所有状态中最大的那个。程序初始化的时候需要对第一行进行预处理,设置dp[0][st][0]=st合法&st中1的个数。这样进行下面的计算的时候,由于0状态肯定是和所有状态兼容的,所以就不会影响计算结果。

代码如下:

#include<iostream>
#include<cstdio>
#include<math.h>
using namespace std;
int dt[105][12];
int dp[101][64][64];//dp[i][now][last]表示填写第i行的时候,i的状态,和i-1行的状态
int m,n;
int num[65];
int shumu;
int biti[11];


bool phf(int i)//用来遍历一行所有的可放置情况
{
    int ii=i;
    shumu=0;
    for(int j=m;j>=1;j--)
    {
        biti[j]=(ii&1);
        if(ii&1) shumu++;
        ii/=2;
    }
    for(int i=1;i<=9;i++)
        for(int j=i+1;j<=10;j++)
        {
            if(biti[j]==1&&biti[i]==1&&j-i<=2)
                return 0;
        }
        return 1;
}

bool fhdx(int i,int h)//判断某行的可行解与地形是否OK
{
    int ii=i;
    for(int j=m;j>=1;j--)
    {
        //cout<<(ii&1)<<endl;
        if((ii&1)==1)
        {
           if(dt[h][j]!=1)
            return 0;
        }
        ii/=2;
    }
    return 1;
}

int main()
{
    std::ios::sync_with_stdio(false);
    cin>>n>>m;
    char ccc;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            cin>>ccc;
            if(ccc=='P') dt[i][j]=1;
            else dt[i][j]=0;
        }
    
//先遍历一行所有的可以放置的情况
    int ke[64];
    int xu=1;
    for(int i=0;i<=(pow(2,m)-1);i++)
    {
        if(phf(i)) {ke[xu]=i;num[xu++]=shumu;}
    }
    //计算发现合法的一行有60种放的方法
    xu--;



    if(n==1)//如果只有一行,需要特判
    {
        int maxn=0;
        for(int i=1;i<=xu;i++)
            if(fhdx(ke[i],1))
            maxn=max(maxn,num[i]);
        cout<<maxn<<endl;
        return 0;
    }

    //获得初始条件dp[2]开头的数据
    for(int i=1;i<=xu;i++)
        for(int j=1;j<=xu;j++)
        {
            int di1=ke[i];
            int di2=ke[j];
            if((di1&di2)==0&&fhdx(di2,2)&&fhdx(di1,1))
                {
                    dp[2][j][i]=num[i]+num[j];
                }
        }

    //开始状态转移
    for(int i=3;i<=n;i++)
    {
        for(int j=1;j<=xu;j++)
        {
            if(fhdx(ke[j],i)==0) continue;//如果该行的地形和放置方式不符合,跳过
            for(int k=1;k<=xu;k++)
                for(int g=1;g<=xu;g++)
                {
                    if((ke[j]&ke[k])==0&&(ke[j]&ke[g])==0&&(ke[k]&ke[g])==0)
                    dp[i][j][k]=max(dp[i-1][k][g]+num[j],dp[i][j][k]);
                }
        }

    }

//找到最多的炮台数目,并输出
    int maxn=0;
    for(int i=1;i<=xu;i++)
        for(int j=1;j<=xu;j++)
        {
            maxn=max(maxn,dp[n][i][j]);
        }
        cout<<maxn<<endl;

    return 0;
}

猜你喜欢

转载自blog.csdn.net/pcfbyssz/article/details/81432276