[PKU1185]炮兵阵地 NOI2001

这题其实是动规+状态压缩。

最基本的压缩想法是:二进制压缩,若第i个地方放了大炮,那么该位就为1。

题目中的列数m很少,m<=10,可以在不考虑地形的情况下计算出一行的所有可能情况,也就是一行的标准情况。那么其余行的状态必定为这些标准状态之一。这些标准状态可以用O(2^m)的时间枚举出来,经实践证明,当m=10时标准状态只有60种,这样一来,每行的状态数不超过60种。

另外一方面,因为一个炮兵会影响到它下面的两行,因此我们在转移的时候需要记下当前行和上一行的状态。用f[i,j,k]表示第i行的状态为标准状态j,第i-1行的状态为标准状态k,那么f[i,j,k]=max{f[i-1,k,u]}+o[j] (其中o[j]表示标准状态j中有多少个炮兵)

最后的答案就是max{f[n,j,k]}

因为这题用到了许多的位运算小技巧,所以在这里顺便提下(其实这些小技巧可以直接看程序):
  1. 要验证某一行的状态i是否可行,也就是验证是否有两个连续的二进制位1:((i<<1)&i==0) && ((i<<2)&i==0)
  2. 要验证某一行是否会和上一行冲突,也就是验证两行有没有同为1的二进制位:!(s[i]&s[j])
  3. 枚举标准状态,只需要一个0~2m-1的for循环。
  4. 要统计出某个数x它的二进制位中有多少个1,可以使用一种基于二分思想的O(1)算法,见程序中的int CountOne(int)。详细的解释可以参考位运算简介及实用技巧(二):进阶篇(1) @ Matrix67大牛。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

int n,m,States,s[60],o[60],f[101][60][60],map[101];
inline int CountOne(int x) {
  x=(x&0x55555555) + ((x>>1) &0x55555555);
  x=(x&0x33333333) + ((x>>2) &0x33333333);
  x=(x&0x0f0f0f0f) + ((x>>4) &0x0f0f0f0f);
  x=(x&0x00ff00ff) + ((x>>8) &0x00ff00ff);
  x=(x&0x0000ffff) + ((x>>16)&0x0000ffff);
  return x;
}
void GetStates() {
  for (int i=0;i<=(1<<m)-1;++i)
    if ((i&i<<1)==0 && (i&i<<2)==0) {
      s[States]=i;
      o[States++]=CountOne(i);
    }
}
int main() {
  memset(f,193,sizeof(f));
  ios::sync_with_stdio(false);
  cin>>n>>m;
  GetStates();
  for (int i=1;i<=n;++i)
    for (int j=1;j<=m;++j) {
      char x;
      cin>>x;
      map[i]<<=1;
      if (x=='H') map[i]|=1;
    }

  for (int j=0;j<States;++j) if (!(s[j]&map[1])) f[1][j][0]=o[j];
  for (int i=2;i<=n;++i)
    for (int j=0;j<States;++j) if (!(s[j]&map[i]))
      for (int k=0;k<States;++k) if (!(s[k]&map[i-1]) && !(s[j]&s[k])) {
        for (int u=0;u<States;++u)  if (!(s[u]&map[i-2]) && !(s[u]&s[k]) && !(s[u]&s[j]))
          f[i][j][k]=max(f[i][j][k],f[i-1][k][u]);
        f[i][j][k]+=o[j];
      }
  int ans=0;
  for (int j=0;j<States;++j)
    for (int k=0;k<States;++k)
      ans=max(ans,f[n][j][k]);
  cout<<ans;
}

Comments