题目链接:点击打开链接
题意:中文题意,就不多赘述了。
题解:一道经典的状压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);
}