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 }

 

posted @ 2021-03-25 00:34  巨大力选手  阅读(42)  评论(0)    收藏  举报