P2704 [NOI2001] 炮兵阵地 (状压dp)
题目链接:https://www.luogu.com.cn/problem/P2704
为了避免 tle,将所有满足炮兵摆放规则的状态存入数组 v,后面只需要对数组 v 进行遍历
为了避免 mle,由于当前状态只和前两行状态有关,所以只需要保留前两行状态即可
f[i][j][k], i 当前行,j 当前行的状态,k 前一行的状态
状态转移方程:
f[i % 3][j][k] = max(f[(i - 1) % 3][k][l] + sum[j], f[i % 3][j][k]);
要注意处理初始状态下的地图,以及状态冲突的情况(仔细点就可以了)
1 #include<iostream> 2 #include<algorithm> 3 #include<stdio.h> 4 #include<vector> 5 #include<string.h> 6 #include<string> 7 #include<math.h> 8 #include<map> 9 #include<set> 10 #include<queue> 11 #include<stack> 12 #include<math.h> 13 #include<functional> 14 using namespace std; 15 #define inf 0x3f3f3f3f 16 #define ll long long 17 const int mod = 20; 18 #define N 100 19 #define M 10 20 int mp[N + 5]; //存储初始地图每一行的状态 21 int v[N + 5]; //存储满足炮兵摆放规则的状态 22 int sum[N + 5]; //存储与 v 对应状态下炮兵摆放的数量 23 int f[3][(1 << M) + 2][(1 << M) + 2]; //f[i][j][k], i 当前行,j 当前行的状态,k 前一行的状态 24 //由于当前行只和前两行有关系,所以只要存储前两行的状态即可,为避免 mle 数组只开 f[3][][] 25 26 int main() { 27 int n, m; 28 cin >> n >> m; 29 char c; 30 for (int i = 0; i < n; i++) { 31 for (int j = 0; j < m; j++) { 32 cin >> c; 33 if (c == 'H') 34 mp[i] = (mp[i] << 1) | 1; //高地状态记为1,不能放置炮兵 35 else 36 mp[i] = mp[i] << 1; //平地状态记为0,能放置炮兵 37 } 38 //mp[i] 即初始地图第i行的状态 39 } 40 //下面这部分是解题的关键 41 //由于炮兵摆放的位置左边的两个和右边的两个都必须为0 42 //所以预处理所有满足该规则的状态,存入 v 数组 43 //并将对应状态的所给予的贡献值(即该状态下1的个数)存入 sum 数组 44 int cnt = 0; 45 for (int i = 0; i < (1 << m); i++) { 46 if ((i & (i << 1)) | (i & (i << 2))) //左1,左 2不能为 1 47 continue; 48 if ((i & (i >> 1)) | (i & (i >> 2))) //右1,右2 不能为 1 49 continue; 50 v[cnt] = i; 51 int ti = i; 52 int ans = 0; 53 while (ti) { // 处理满足炮兵摆放规则的状态 所给的贡献值 54 if (ti % 2)ans++; 55 ti /= 2; 56 } 57 sum[cnt] = ans; // 注意:sum 数组 要和 v 数组一一对应 58 cnt++; 59 60 } 61 62 /*debug 63 or (int i = 0; i < cnt; i++) 64 cout << v[i] << " "; 65 cout << endl; 66 for (int i = 0; i < (1 << m); i++) { 67 cout << sum[i] << " "; 68 } 69 cout << endl; 70 */ 71 72 // 由于上面已经预处理了所有满足摆放规则的状态,所以下面只用对存储的 v 进行遍历 73 74 int ma = -inf; 75 76 //处理第一行 77 for (int i = 0; i < cnt; i++) { 78 if (v[i] & mp[0])continue; //v[i]判断是否和初始地图的第1行冲突 79 f[0][i][0] = sum[i]; 80 } 81 //处理第二行 82 for (int i = 0; i < cnt; i++) { 83 if (v[i] & mp[1])continue; //v[i]判断是否和初始地图的第2行冲突 84 for (int j = 0; j < cnt; j++) { //遍历前一行状态 85 if ((v[j] & mp[0]) | (v[i] & v[j]))continue; //判断v[j]是否和初始地图的第1行冲突 86 f[1][i][j] = sum[i] + sum[j]; 87 } 88 } 89 //处理剩余行 90 for (int i = 2; i < n; i++) { //枚举行 91 92 for (int j = 0; j < cnt; j++) { //枚举当前行的状态 93 if (v[j] & mp[i])continue; //判断是否和初始地图第 i 行状态冲突 94 95 for (int k = 0; k < cnt; k++) { //枚举前一行的状态 96 if (v[k] & mp[i - 1])continue; //判断是否和初始地图第 i - 1 行冲突 97 if (v[j] & v[k])continue; //判断当前行的状态 v[j] 是否和它前一行的状态 v[k] 冲突 98 99 for (int l = 0; l < cnt; l++) { //枚举前前一行的状态 100 if (v[l] & mp[i - 2])continue; //同上 101 if ((v[k] & v[l]) | (v[j] & v[l]))continue; //同上 102 103 f[i % 3][j][k] = max(f[(i - 1) % 3][k][l] + sum[j], f[i % 3][j][k]); // 状态转移 104 /*cout << f[i][j][k] << " ";*/ 105 } 106 } 107 } 108 /* cout << endl;*/ 109 } 110 111 for (int i = 0; i < cnt; i++) { 112 for (int j = 0; j < cnt; j++) { 113 /* cout << f[n - 1][i][j] << " ";*/ 114 ma = max(ma, f[(n - 1) % 3][i][j]); 115 } 116 } 117 cout << ma << endl; 118 119 return 0; 120 }
我一定很聪明,但不会绝顶,这是我最后的倔强!!!

浙公网安备 33010602011771号