***POJ1185 炮兵阵地 ACM解题报告(状压dp入门题)

这题是个入门的状压dp(虽然对我来说好难啊,坑了一下午终于过了。。。)难得一个中文题,还这么难

注意点挺多的,首先是要开数组记录所有情况,然后记录每种情况的炮兵数,很多处理类似于前一题3254,可以参考,不过这题更难一点,因为需要考虑两行。

所以得开个三维数组记录情况,d[i][j][k],i是当前行数,j是第i行的情况,k是i-1行的情况。

状态转移方程:d[i][j][k]=max(d[i][j][k],d[i-1][k][h]+num[j]),需要注意几个限制state[j]&b[i]==0,state[j]&state[k]==0,state[j]&state[h]==0,边界是d[0][j][k]=num[j]。

其中需要注意的是num数组是记录每种情况有多少个炮兵,就是要记录一个二进制数之中有多少个1,位运算优先级低,一定要加括号(我就是被拿了学长写的代码然后没加括号,看了一下午。。。虽然自己也有个手误。)

#include<iostream>
#include<cstdio>
#include<cctype>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<string>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<stack>
using namespace std;
#define MAX 105
typedef long long LL;
const double pi=3.141592653589793;
const int INF=1e9;
const double inf=1e20;
const double eps=1e-6;
int top,b[105],d[105][100][100];//第一维是第i行,第二维是第i行的状态,第三维是i-1行的状态,存储的是能有多少个炮兵
int state[100];//记录一行上满足条件的总的情况种类
int num[100];//记录每种情况有多少个炮兵
int n,m;
bool ok(int x){
	if(x&(x<<1)) return false;//相邻2隔内不能有炮兵
	if(x&(x<<2)) return false;
	return true;
}
int bitcount(int x) { 
	return x == 0 ? 0 : bitcount(x/2) + (x&1);//&优先级低,要加括号(ps.被学长阴掉了)
}
void init(){
	int total=(1<<m);
	top=0;
	for(int i=0;i<total;i++){
		if(ok(i)){
			num[top]=bitcount(i);
			state[top++]=i;
		}
	}
}
bool fit(int x,int y){
	if(x&b[y]) return false;
	return true;
}
int main(){
	char a[105][20];
	cin>>n>>m;
	init();
	memset(b,0,sizeof(b));
	memset(d,-1,sizeof(d));
	for(int i=0;i<n;i++){
		getchar();
		scanf("%s",a[i]);
		for(int j=0;j<m;j++){
			if(a[i][j]=='H') b[i]+=(1<<j);
		}
	}
	for(int i=0;i<top;i++){
		if(fit(state[i],0)){
			for(int j=0;j<top;j++){
				d[0][i][j]=num[i];//第一行的情况
			}
		}
	}
	int maxn=0;
	for(int i=1;i<n;i++){
		for(int j=0;j<top;j++){
			if(fit(state[j],i)){//i行满足的情况
				for(int k=0;k<top;k++){
					if(state[j]&state[k]) continue;
					for(int h=0;h<top;h++){//i-2行满足的情况
						if(state[h]&state[j]) continue;
						if(state[h]&state[k]) continue;
						if(d[i-1][k][h]==-1) continue;
						d[i][j][k]=max(d[i][j][k],d[i-1][k][h]+num[j]);
					}
				}
			}
		}
	}
	for(int i=0;i<n;i++){
		for(int j=0;j<top;j++){
			for(int k=0;k<top;k++) if(maxn<d[i][j][k]) maxn=d[i][j][k];//一定要枚举所有情况,如果只有一行的话,最大值就在第一行里,不能在上面计算过程中                                                                                     找maxn
		}
	}
	printf("%d\n",maxn);
    	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值