题目链接: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\n",ans);
return 0;
}