题目:https://www.acwing.com/problem/content/294/
题意:给一个包含H和P的网格图,P代表可以放东西,H代表不能放东西,并且俩个东西不能互相攻击到,攻击范围如下图,问最多能够放多少东西。
题解:状压DP,这里要开三维,f[i][j][k],第一维是题目给的n,第二维代表上一层的所有状态,第三维代表本层的所有状态,这样就可以表示了,这样开三维空间,会炸内存,所以第一维用滚动数组(写法和普通数组一样,只是用数组的时候第一维&1就ok了)
y总讲解:
#include <algorithm>
#include <bitset>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <deque>
#include <functional>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>
//#include <unordered_map>
//#include <unordered_set>
//#include <bits/stdc++.h>
//#define int long long
#define pb push_back
#define PII pair<int, int>
#define mpr make_pair
#define ms(a, b) memset((a), (b), sizeof(a))
#define x first
#define y second
typedef long long ll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int N = 102, M = 1 << 10;
using namespace std;
vector<int> vec;
int f[2][M][M]; //滚动数组,第二维是上一层的状态,第三层是本层的状态
int ma[N], cnt[N]; // ma存题目给的图,cnt存这个状态放了炮的数量
int n, m;
int check(int num) {
for (int i = 0; i < m; i++) {
if ((num >> i & 1) && (num >> i + 1 & 1)) return 0;
if ((num >> i & 1) && (num >> i + 2 & 1)) return 0;
}
return 1;
}
int get1(int num) {
int s = 0;
while (num) {
s += num % 2;
num = num / 2;
}
return s;
}
signed main(int argc, char const *argv[]) {
cin >> n >> m;
for (int i = 1; i <= n; i++) {
string s;
cin >> s;
int ant = 0;
for (int j = 0; j < s.size(); j++)
if (s[j] == 'H') ant += 1 << j;
ma[i] = ant;
}
for (int i = 0; i < 1 << m; i++)
if (check(i)) vec.emplace_back(i), cnt[i] = get1(i);
int len = vec.size();
int maxx = 0;
for (int i = 1; i <= n; i++) { //枚举层数
for (int j = 0; j < len; j++) { //枚举上一层的状态
for (int k = 0; k < len; k++) {//枚举本层的状态
for (int u = 0; u < len; u++) {//枚举上上层的状态
int a = vec[j], b = vec[k], c = vec[u];//a代表上一层状态,b代表本层状态,c代表上上层状态
if ((a & b) == 0 && (a & c) == 0 && (b & c) == 0 &&
(a & ma[i - 1]) == 0 && (b & ma[i]) == 0) {//保证任何俩个状态不冲突,且不与题目给的图冲突,注意&运算优先级比==小
f[i & 1][j][k] =
max(f[i & 1][j][k], f[i - 1 & 1][u][j] + cnt[b]);//找上一层j状态下的再上一层u状态,这里用下标了,用状态来存也行,但注意其它有些地方也要变
// maxx = max(f[i][j][k], maxx);
// f[i][a][b]=max(f[i-1][0][c]+1,f[i][a][b]);
maxx = max(f[i & 1][j][k], maxx); //取最大
}
}
}
}
}
//也可以最后暴力找一遍
// maxx = 0;
// for (int j = 0; j < len; j++) {
// for (int k = 0; k < len; k++) {
// // printf("%d ", f[n][j][k]);
// maxx = max(f[n & 1][j][k], maxx);
// }
// }
cout << maxx << endl;
//也可以在上面第一层for循环的时候<=n+2,多来两层,直接输出这个答案
// cout << f[n + 2 & 1][0][0] << endl;
return 0;
}