POJ - 1185 炮兵阵地 (状压DP)

题目链接:点击打开链接

题意:中文题意,就不多赘述了。

题解:一道经典的状压DP。DP【i】【j】【k】表示第 i 行,第j个合法状态 ,第i-1 行第k个合法状态的合法安置数目,

所以我们要预处出所有合法状态。

此处我用显然来代替我得思路。

显然是正确的。

我在代码注释里面写的非常详细。

看代码把:

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

int Map[1 << 11];  //用一个2进制数表示第i行的地图状态,0为空地,1为山地 
int stk[1 << 11];  // 二进制记录合法状态。 
int dp[101][64][64]; //dp【i】【j】【k】 表示第i行的j状态,i-1行的k状态能放的最大数目。 
int sum[66];   // 某个二进制状态的1的个数。1代表放炮,0代表不放炮 
int cnt = 0 ,n,m;  // cnt 表示和合法状态有多少个。 
int getsum(int i){
	int sum = 0;   // 记录i中1个数 
	while(i){  // 当1不为0时 
		if(i&1) sum ++;  // 如果最后一位为1 sum ++ 
		i = i >> 1;   // i 右移 1位 
	}
	return sum;  // 返回sum 
}
int judge(int x){
	if(x & (x << 1)) return 0;   // 若x和右边边第一个冲突 , 比如二进制 011 左移1为 变成 110 001&110 = 1 代表不合法 
	if(x & (x << 2)) return 0;   // 若x和右边第二个冲突 原理同上 
	return 1;    // 否者为合法状态。 
}
void init(){
	for(int state = 0 ; state < (1 << m) ; state ++){  // 列数为m 所有一共有(1<<m)种状态 
		if(judge(state)){  // 判断状态是否合法。 
			stk[cnt] = state;  // 记录合法状态 state 
			sum[cnt++] = getsum(state);  // 统计该状态有多个炮。 
		}
	}
}
int main(){
	char te;
	scanf("%d%d",&n,&m); // 输入行和列的大小 
	for(int i = 0 ; i < n ; i ++){   
		for(int j = 0 ; j < m ; j ++){
			cin >> te;
			if(te == 'H')  // 若为H表明这个地方为山。 0 | 1 = 1 
				Map[i] |= (1 << j);
		}
	}
	memset(dp,0,sizeof(dp)); // 清空DP数组 
	init(); // 预处理出所有合法状态。 
	for(int i = 0; i < cnt ; i ++){   // 处理第0行 
		if(Map[0] & stk[i]) continue;  // 若第i个状态stk[i]和第0行的地图冲突 就跳过去,就是有山的地方安置了炮 
		dp[0][i][0] = sum[i];  // 让 第0行i状态为 第i个状态1的个数 
	}
	for(int i = 0 ; i < cnt ; i ++){  // 处理第1行 
		if(Map[1] & stk[i]) continue;  // 若第i个状态stk[i]和第1行的地图冲突 ,就跳过去。 
		for(int j = 0 ; j < cnt ; j ++){  // 枚举第0行的所有状态。 
			if(Map[0] &stk[j]) continue;   // 若第j个状态stk[j]和第0行的地图冲突 ,就1跳过去 
			if(stk[i] &stk[j]) continue;   // 若第1行的i状态stk[i] 和 第0行的j状态stk[j] 冲突就跳过去。就是大炮互相能打到。 
			dp[1][i][j] = max(dp[1][i][j],dp[0][j][0]+sum[i]);  // 更新 第一行的i状态第0行的j状态 
		}
	}
	for(int i = 2 ; i < n ; i ++){  // 处理第2行以及第2行以上。 
		for(int state = 0 ; state < cnt ; state ++){   // 枚举第i行的状态 
			if(Map[i] & stk[state]) continue;    // 若第i行的状态stk【state】 和 第i行的地图相冲突就 跳过去 
			for(int last = 0 ; last < cnt ; last ++){  // 枚举 i - 1 行的状态 
				if(Map[i-1] & stk[last]) continue;  // 若第i-1行的状态stk【last】  和 i-1行的地图冲突就跳过去 
				if(stk[state] & stk[last]) continue;  // 若第i行的状态stk【state】 和 第i-1行的状态stk【last】冲突 
				for(int Llast = 0 ; Llast < cnt ; Llast ++){  // 枚举 i - 2 行的 状态 
					if(Map[i-2] & stk[Llast]) continue; // 若第i-2行的状态stk【Llast】 和 第i-2行的地图冲突 
					if(stk[Llast] & stk[last]) continue;  // 若i-2 行的状态 stk【Llast】 和 第i - 1行的状态stk【last】冲突 
					if(stk[Llast] & stk[state]) continue;  // 若 i-2 行的状态stk【Llast】 和 第 i 行状态 stk【state】冲突 
					dp[i][state][last] = max(dp[i][state][last],dp[i-1][last][Llast]+sum[state]);  // 更新DP【i】【state】【last】。 
				}
			}
		}
	}
	int ans = 0 ; // 找出最大值。 
	for(int i = 0 ; i < cnt ; i ++){
		for(int j = 0 ;j < cnt ; j ++){
			ans = max(dp[n-1][i][j],ans);
		}
	}
	printf("%d\n",ans);
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值