poj1185 炮兵阵地

题目链接:https://vjudge.net/problem/POJ-1185

题意:nxm网格,每个格子可能为平原P或山地H。只有平原能放炮兵,炮兵的攻击范围为上下左右各延伸2格的一个十字,一个炮兵不能再其他炮兵的攻击范围内,求最多能摆放多少个炮兵

这题有点麻烦。整个题目分为预处理+状压dp两个部分

首先要两个预处理:一是没有炮兵在别的攻击范围内的状态s,二是状态s在第i行能不能放(因为炮兵只能放在平原P上)

然后是状压dp。设二进制表示下为1是放了炮兵,为0是没有放。这题当前行会被前两行影响,不再像之前做的题目,只有i-1行会影响第i行。如果还是设f[i][s]的话,要考虑f[i-1][s'],f[i-2][s''],我没想出来这个转移该怎么写。一个更好的状态设计是: f[i][s1][s2]表示考虑到第i行,第i行的状态为s1,第i-1行的状态为s2时,前i行最多能放多少个炮兵,则有:

f[i][s1][s2]=max(f[i-1][s2][s3])+__builtin_popcount(s1),此处s1,s2,s3都是合法状态且互相&均为0

#include<iostream>
#include<algorithm>
using namespace std;

int f[110][(1<<10)][(1<<10)],b[(1<<10)],c[15],v[110][(1<<10)];
int n,m,i,j,k,s1,s2,s3,s;
char ch[110][12];

int main(){
	cin>>n>>m;
	for (i=1;i<=n;i++)
	  for (j=0;j<m;j++) cin>>ch[i][j];
	for (s=0;s<(1<<m);s++){
	  for (i=0;i<m;i++) c[i]=s&(1<<i);
	  c[m+2]=1; j=0;
	  while (c[j]==0) j++; j++;
	  int flag=0;
	  while (j<m+2){
	  	int num=0;
	  	while (c[j]==0) {j++; num++;}
	  	if (num<2) flag=1;
	  	j++;
	  }
	  b[s]=(flag==0)?1:0;
	}
	v[0][0]=1;
	for (i=1;i<=n;i++)
	  for (s=0;s<(1<<m);s++){
	  	int flag=0;
	  	for (j=0;j<m;j++)
	  	  if (ch[i][j]=='H'&&s&(1<<j)) flag=1;
	  	v[i][s]=(flag==0)?1:0;
	  }
	for (s=0;s<(1<<m);s++)
	  if (b[s]&&v[1][s]) f[1][s][0]=__builtin_popcount(s);
	for (i=2;i<=n;i++)
	  for (s1=0;s1<(1<<m);s1++)
	    if (b[s1]&&v[i][s1])
	      for (s2=0;s2<(1<<m);s2++)
	        if (b[s2]&&v[i-1][s2]&&((s1&s2)==0))
	          for (s3=0;s3<(1<<m);s3++)
	            if (b[s3]&&v[i-2][s3]&&((s2&s3)==0)&&((s1&s3)==0))
	              f[i][s1][s2]=max(f[i][s1][s2],f[i-1][s2][s3]+__builtin_popcount(s1));
    int ans=0;
    for (s1=0;s1<(1<<m);s1++)
      for (s2=0;s2<(1<<m);s2++) ans=max(ans,f[n][s1][s2]);
    cout<<ans<<endl;
	return 0;
}

 

posted @ 2020-09-18 16:01  coastal_taipan  阅读(129)  评论(0编辑  收藏  举报