状压DP之炮兵阵地

题目

原题来自:\(NOI 2001\)

司令部的将军们打算在\(N*M\)
的网格地图上部署他们的炮兵部队。一个\(N*M\)的地图由\(N\)\(M\)列组成,地图的每一格可能是山地(用 H表示),也可能是平原(用 P 表示)。在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队),如果在地图中的灰色所标识的平原上部署一支炮兵部队,它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。其他位置攻击不到。炮兵的攻击范围不受地形的影响。现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队。

输入格式

第一行包含两个由空格分割开的正整数,分别表示\(N\)\(M\)
接下来的\(N\)行,每一行含有连续的\(M\)个字符(P 或者 H),中间没有空格。按顺序表示地图中每一行的数据。

输出格式

仅一行,包含一个整数\(K\),表示最多能摆放的炮兵部队的数量。

样例

样例输入

5 4
PHPP
PPHH
PPPP
PHPP
PHHP

样例输出

6

思路

这道题是一个很有趣的状压DP,由题意可知,对当前状态影响的有当前行状态,上一行状态,以及上上行的状态,对该行都有影响,所以,很显然我们枚举第\(i\)行,\(i-1\)行以及\(i-2\)的状态,对于任意一行(假设为\(i\)),那么我们对第\(i\)行进行合法化判断(仅对当前行),记录合法状态以及数目,方便下面枚举,还需要注意的是地图上的山地,需要单独开一个数组记录,与枚举状态求&,然后我们设\(F\)的三维数组,即\(F[i][j][k]\)代表第i行第j种状态从上一行第k种状态转移过来的最优解,所以有\(f[i][w][k]=max(f[i][w][k],f[i-1][k][j]+Q(w))\),\(f[i][w][k]\)上一状态为\(f[i-1][k][j]\),继承或求较大值更新

如果还是不明白的话,代码奉上



#include<bits/stdc++.h>
using namespace std;
const int maxn=1<<9;
int f[100+10][maxn][maxn];//第i行第j种状态从上一行第k种状态转移过来的最优解
int a[100+10];//记录地图山丘的数量
int state[maxn];//记录合法的状态
int tot,n,m;
int lowbit(int x){//求最低位1
	return x&-x;
}
int Q(int x){//判断第x种合法状态下的炮兵个数
	x=state[x];
	int cnt=0;
	for(int i=x;i;i-=lowbit(i))cnt++;
	return cnt;
	
}

bool judge(int x){//判断状态x的合法性
	if(x&(x<<1) || x&(x<<2))return 0;
	return 1;
}
int main(){
	
	cin>>n>>m;
	char ch;
	for(int i=1;i<=n;i++){
		for(int j=0;j<m;j++){
			scanf(" %c ",&ch);
			if(ch=='H')a[i]+=(1<<j);//记录山丘位置
		}
	}
	for(int i=0;i<(1<<m);i++){//记录合法状态
		if(judge(i))state[++tot]=i;
	}
	for(int i=1;i<=tot;i++){//初始化第一行i种状态下的最优解,即为当前状态下的炮兵数量
		if((a[1] & state[i])==0)f[1][i][1]=Q(i);
	}
	for(int i=2;i<=n;i++){//枚举第i行
		for(int j=1;j<=tot;j++){//i-2行
			if(state[j] & a[i-2])continue;//判断第i-2行
			for(int k=1;k<=tot;k++){//枚举第i-1行
				if(state[k] & a[i-1])continue;//判断第i-1行
				if(state[k] & state[j])continue;//判断第i-1行与第i-2行
				for(int w=1;w<=tot;w++){
					if(state[w] & a[i])continue;//判断第i行
					if(state[w] & state[k])continue;//判断第i行和第i-1行
					if(state[w] & state[j])continue;//判断第i行和第i-2行
					f[i][w][k]=max(f[i][w][k],f[i-1][k][j]+Q(w));//合法情况转移
				}
			}
		}
	}
	int ans=0;
	for(int i=1;i<=tot;i++){//求解
		for(int j=1;j<=tot;j++){
			if(a[n-1] & state[i])continue;
			if(a[n] & state[j])continue;
			if(state[i] & state[j])continue;
			ans=max(f[n][j][i],ans);
		}
	}
	cout<<ans<<endl;

}


posted @ 2020-06-25 21:41  sodak  阅读(155)  评论(0编辑  收藏  举报