POJ--1185--炮兵阵地(状态压缩DP)

题目链接:http://poj.org/problem?id=1185

题意:

给 N,M(N<=100,M<=10),之后给出一个 N*M 由 "H"、"P" 组成的一个矩阵,H--代表山地,P--代表平原,一架炮的攻击范围如图所示;炮可以攻击 H(山地),但只能架在P(平原),在防止误伤的前提下(即炮不可以架在攻击范围之内,但攻击范围可以重叠),求整个区域最多能摆多少架炮?不是求方案数,不是求方案数,不是求方案数。

思路:

1.仔细分析问题后,发现该问题可以抽象为 0 1 的子问题,即假设 1 为有炮,0 为没有炮,最终答案是一个 N*M 的0 1矩阵中1的个数。

 既然如此,先将N*M的H,P矩阵转换为01矩阵,再将其按行压缩为十进制的一维数组保。代码如下

char c;
for(int i=1;i<=N;++i)
{
    int Sum=0;
    for(int j=M-1;j>=0;--j)
    {
        cin>>c;
        if(c=='P')
            Sum+=(1<<j);//这里是位运算,如果不会参考我的上一篇博客,进行运算是括号括起来,再做为整体进行运算
    }
    a[i]=Sum;
}

2.再进行防止误伤的分析时,将每一个炮分为横向防止误伤,纵向防止误伤两种情况来处理。对横向防误伤可以进行预处理,只将不会进行相互攻击的情况提前找出来(即找出两个1之间有两个即两个以上0的情况), 再看条件M<=10,最多有1024中 0 1的全排列,所以符合的状态最多有60种。代码如下

init(0,0);//main()中的接口
//普通递归函数求符合解,并保存
void init(int i,int flag)
{
    if(i==M)
    {
        sta[sum]=0;//sta[]为保存符合的状态
        sta_n[sum]=0;//sta_n[]为保存符合状态中1的个数
        int n=0;
        for(int i=0;i<M;++i)
        {
            if(num[i])//num[]为递归保存的01状态数组
            {
                ++n;
                sta[sum]+=(1<<i);
            }
        }
        sta_n[sum]=n;
        ++sum;//符合状态总数
    }
    else
    {
        if(flag==0)//flag为之前1的距离
        {
            num[i]=0;
            init(i+1,0);
            num[i]=1;
            init(i+1,2);
        }
        else
        {
            num[i]=0;
            init(i+1,flag-1);
        }
    }
}

3.对于处理纵向防误伤,由状态转移方程处理:dp[i][j][k]=max(dp[i][j][k],dp[i-1][k][p]+sta_n[j]);(就是把第 i 行前两行(因为当且仅当第 i 行的前两行会影响第 i 行)的所有状态枚举出来,求出最值)

dp[i][j][k]:表示第 i 行,第 j 种状态在第 i-1 行是第 k 种状态的情况下最多的炮台数目;第1行和第2行另行处理;最大值不一定是在第 N 行出现(不是求方案数,所以要求全体最值)。代码如下

    int ans=0;
    if(N>=1)
    {
        for(int j=0;j<sum;++j)
        {
            if((sta[j]|a[1])==a[1])//如不理解此处含义,请翻上篇博客
            for(int k=0;k<sum;++k)
            {
                dp[1][j][k]=sta_n[j];
                ans=max(ans,dp[1][j][k]);
            }
        }
    }
    if(N>=2)
    {
        for(int j=0;j<sum;++j)
        {
            if((sta[j]|a[2])==a[2])
            for(int k=0;k<sum;++k)
            {
                if((sta[k]&sta[j])==0)
                {
                    dp[2][j][k]=dp[1][k][0]+sta_n[j];
                    ans=max(ans,dp[2][j][k]);
                }
            }
        }
    }
    for(int i=3;i<=N;++i)
    {
        for(int j=0;j<sum;++j)
        {
            if((sta[j]|a[i])==a[i])
            for(int k=0;k<sum;++k)
            {
                if((sta[j]&sta[k])==0)
                for(int p=0;p<sum;++p)
                {
                    if((sta[j]&sta[p])==0)
                    {
                        dp[i][j][k]=max(dp[i][j][k],dp[i-1][k][p]+sta_n[j]);
                        ans=max(ans,dp[i][j][k]);
                    }
                }
            }
        }
    }

AC代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int num[20],sum=0,sta[65],sta_n[65];
int a[110],dp[110][65][65];
int N,M;
void init(int i,int flag)
{
    if(i==M)
    {
        sta[sum]=0;
        sta_n[sum]=0;
        int n=0;
        for(int i=0;i<M;++i)
        {
            if(num[i])
            {
                ++n;
                sta[sum]+=(1<<i);
            }
        }
        sta_n[sum]=n;
        ++sum;
    }
    else
    {
        if(flag==0)
        {
            num[i]=0;
            init(i+1,0);
            num[i]=1;
            init(i+1,2);
        }
        else
        {
            num[i]=0;
            init(i+1,flag-1);
        }
    }
}
int main()
{
    char c;
    cin>>N>>M;
    init(0,0);
    for(int i=1;i<=N;++i)
    {
        int Sum=0;
        for(int j=M-1;j>=0;--j)
        {
            cin>>c;
            if(c=='P')
                Sum+=(1<<j);
        }
        a[i]=Sum;
    }
    int ans=0;
    if(N>=1)
    {
        for(int j=0;j<sum;++j)
        {
            if((sta[j]|a[1])==a[1])
            for(int k=0;k<sum;++k)
            {
                dp[1][j][k]=sta_n[j];
                ans=max(ans,dp[1][j][k]);
            }
        }
    }
    if(N>=2)
    {
        for(int j=0;j<sum;++j)
        {
            if((sta[j]|a[2])==a[2])
            for(int k=0;k<sum;++k)
            {
                if((sta[k]&sta[j])==0)
                {
                    dp[2][j][k]=dp[1][k][0]+sta_n[j];
                    ans=max(ans,dp[2][j][k]);
                }
            }
        }
    }
    for(int i=3;i<=N;++i)
    {
        for(int j=0;j<sum;++j)
        {
            if((sta[j]|a[i])==a[i])
            for(int k=0;k<sum;++k)
            {
                if((sta[j]&sta[k])==0)
                for(int p=0;p<sum;++p)
                {
                    if((sta[j]&sta[p])==0)
                    {
                        dp[i][j][k]=max(dp[i][j][k],dp[i-1][k][p]+sta_n[j]);
                        ans=max(ans,dp[i][j][k]);
                    }
                }
            }
        }
    }
    printf("%d
",ans);
    return 0;

}

  

原文地址:https://www.cnblogs.com/l1l1/p/8553211.html