Time Limit: 2000MS | Memory Limit: 65536K | |
Total Submissions: 25502 | Accepted: 9836 |
Description
![](http://poj.org/images/1185_1.jpg)
如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。图上其它白色网格均攻击不到。从图上可见炮兵的攻击范围不受地形的影响。
现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队。
Input
接下来的N行,每一行含有连续的M个字符('P'或者'H'),中间没有空格。按顺序表示地图中每一行的数据。N <= 100;M <= 10。
Output
Sample Input
5 4
PHPP
PPHH
PPPP
PHPP
PHHP
Sample Output
6
Source
思路:
如果小伙伴们对状压dp还不是很了解的话,大家可以先考虑考虑做做这个题并参考参考我的详解:http://blog.csdn.net/mengxiang000000/article/details/51075506
1、设定dp【i】【j】【k】表示现在dp过程到了第i行,第i行状态为j第i-1行状态为k的最大放置炮台的个数。对于状态来讲,我们使用十进制数来保存,其含义为:其转化成2进制之后,对应位数字为1表示这个位子上放置炮台,数字为0表示这个位子上不放置炮台。
2、那么不难理解:
dp【i】【j】【k】=max(dp【i】【j】【k】,dp【i-1】【k】【l】+第i行状态j放置的炮台个数);
虽然这个状态转移方程式一点问题都没有,但是呢,它的设定会超内存。所以呢,我们再次设定:
dp【i】【j】【k】表示现在dp过程到了第i行,第i行的可行状态编号为j,第k行的可行状态编号为k的最大放置炮台的个数
e.g:某一行:PHHP
可行方案有三种:
1
8
9
对应我们给其编号为;
1 1
2 8
3 9
因为可行方案并不会太多,所以我们可以更多的释放内存。
3、那么我们在输入完图之后,初始化搞定这个可行状态的保存问题:
①设定tmp=这一行H的情况,e.g:PHPP,tmp=2;(我们设定最左边的是第一位)
②枚举这一行所有可能的状态j【(0<=j<(1<<m)】,然后判断这种状态是否合法,首先,j&tmp==0,表示在H的地方,我们没有放置不合法的炮台。
③然后我们将这种状态保存入数组a【i】【contz】,并且记录当前状态中有多少个1(就是有多少个炮台),记录在add【i】【contz】中。
对应代码:
int judge(int j,int k,int l)
{
if((j&k)!=0)return 0;
if((j&l)!=0)return 0;
if((k&l)!=0)return 0;
return 1;
}
int end=(1<<m);
for(int i=0;i<n;i++)
{
int tmp=0;
int contz=0;
for(int j=0;j<m;j++)
{
if(aa[i][j]=='H')
{
tmp+=(1<<j);
}
}
for(int j=0;j<end;j++)
{
if(judgerow(j)==1)
{
if((j&tmp)==0)
{
int sum=0;
int tmpp=j;
while(tmpp)
{
if((tmpp&1)!=0)sum++;
tmpp/=2;
}
a[i][contz]=j;
add[i][contz++]=sum;
}
}
}
kk[i]=contz;
}
4、然后我们进行状态转移,我们从第二行开始搞定这个问题(如果输入只有一行,我们单独处理)
①枚举第i行的状态j和i-1行的状态k,得到dp【i】【j】【k】的三个元素i,j,k。
②然后枚举一个状态l,得到dp【i-1】【k】【l】的三个元素i-1,k,l。
③然后判断一下j,k,l三个状态有没有上下矛盾的情况,如果没有,维护dp【i】【j】【k】的值。
对应代码:
int judgerow(int j)
{
if((j&(j<<1))!=0)return 0;
if((j&(j<<2))!=0)return 0;
if((j&(j>>1))!=0)return 0;
if((j&(j>>2))!=0)return 0;
return 1;
}
for(int i=1;i<n;i++)
{
for(int jj=0;jj<kk[i];jj++)
{
for(int ll=0;ll<kk[i-1];ll++)
{
int j=jj;
int k=ll;
int addd=add[i][jj];
if(i==1)
{
addd+=add[i-1][ll];
if(judge(a[i][j],a[i-1][k],0)==1)
{
dp[i][j][k]=max(dp[i][j][k],addd);
}
continue;
}
for(int pp=0;pp<kk[i-2];pp++)
{
int l=pp;
if(judge(a[i][j],a[i-1][k],a[i-2][l])==1)
{
dp[i][j][k]=max(dp[i][j][k],dp[i-1][k][l]+addd);
}
}
}
}
}
5、整个思路和核心代码搞定了之后:
ans=max(dp【n-1】【i】【j】);
维护一下即可。
完整Ac代码:
#include<stdio.h>
#include<iostream>
#include<string.h>
using namespace std;
//int dp[150][1024][1024];
int dp[150][120][120];
int kk[150];
int add[150][1050];
int a[150][1050];
char aa[150][20];
int n,m;
int judge(int j,int k,int l)
{
if((j&k)!=0)return 0;
if((j&l)!=0)return 0;
if((k&l)!=0)return 0;
return 1;
}
int judgerow(int j)
{
if((j&(j<<1))!=0)return 0;
if((j&(j<<2))!=0)return 0;
if((j&(j>>1))!=0)return 0;
if((j&(j>>2))!=0)return 0;
return 1;
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
memset(dp,0,sizeof(dp));
for(int i=0;i<n;i++)
{
scanf("%s",aa[i]);
}
int end=(1<<m);
for(int i=0;i<n;i++)
{
int tmp=0;
int contz=0;
for(int j=0;j<m;j++)
{
if(aa[i][j]=='H')
{
tmp+=(1<<j);
}
}
for(int j=0;j<end;j++)
{
if(judgerow(j)==1)
{
if((j&tmp)==0)
{
int sum=0;
int tmpp=j;
while(tmpp)
{
if((tmpp&1)!=0)sum++;
tmpp/=2;
}
a[i][contz]=j;
add[i][contz++]=sum;
}
}
}
kk[i]=contz;
}
for(int i=1;i<n;i++)
{
for(int jj=0;jj<kk[i];jj++)
{
for(int ll=0;ll<kk[i-1];ll++)
{
int j=jj;
int k=ll;
int addd=add[i][jj];
if(i==1)
{
addd+=add[i-1][ll];
if(judge(a[i][j],a[i-1][k],0)==1)
{
dp[i][j][k]=max(dp[i][j][k],addd);
}
continue;
}
for(int pp=0;pp<kk[i-2];pp++)
{
int l=pp;
if(judge(a[i][j],a[i-1][k],a[i-2][l])==1)
{
dp[i][j][k]=max(dp[i][j][k],dp[i-1][k][l]+addd);
}
}
}
}
}
if(n==1)
{
int ans=0;
for(int i=0;i<kk[0];i++)
{
ans=max(add[0][i],ans);
}
printf("%d\n",ans);
continue;
}
int ans=0;
for(int i=0;i<kk[n-1];i++)
{
for(int j=0;j<kk[n-2];j++)
{
ans=max(dp[n-1][i][j],ans);
}
}
printf("%d\n",ans);
}
}