luogu2704 炮兵阵地 状态压缩DP

时间:2022-07-02 16:50:26

题目大意:一个N*M的地图由N行M列组成,地图的每一格可能是山地(用“H” 表示),也可能是平原(用“P”表示),在每一格平原地形上最多可以布置一支炮兵部队,能攻击到的区域:沿横向左右各两格,沿纵向上下各两格。保证任何两支炮兵部队之间不能互相攻击时,最多能放置的炮兵数。N<=100,N<=10

动规先要确定方向,我们规定其为从上到下。每一排的最优值与其前两排的各个炮兵的放置位置都有关,所以为了使得DP以后的排时能够找到其对应的前两排的各个炮兵的放置位置所对应的最优值,DP内的参数有:

  1. 当前的排数i
  2. 当前排的炮兵状态curS
  3. 上一排的炮兵状态prevS

定义上两排的炮兵状态为grandS,这样,对于每个满足二进制数内两个1距离超过2的curS,prevS,grandS(因为M是固定的,所以可以在DP前将其算好,叫做RowSs)递归式为:

foreach DP(i, curS, prevS) (curS属于i排平原 且 prevS属于i-1排平原 且 curS∩prevS=空),其值 = max foreach DP(i-1, prevS, grandS)+curS内1的数量 (grandS属于i-2排平原 且 curS∩grandS为空 且 prevS∩grandS为空)

curS内1的数量可以在算完RowSs时一起求出。

一切数组从0开始,DP开始时先特殊处理i=0和1的情况,避免以后在各种特判中搞晕。

DP要用三层滚动数组保存,否则应该会爆空间。

注意:

  • 计算RowSs的过程就用二进制的枚举子集,不要想其它方法浪费时间。
  • 每到一个i就要将其对应的滚动数组的那一层清空!!!!!!!!!!!!!!!!!!!!
  • 注意对N和M下定义,不要搞反了,否则再好的算法也只能得20分。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdarg>
#include <cstdlib>
#include <iostream>
#include <bitset>
using namespace std; const int MAX_N = 110, MAX_M = 10, NINF = 0xcfcfcfcf;
int Map[MAX_N], DP[3][1 << (MAX_M + 1)][1 << (MAX_M+1)], ColSs[1<<(MAX_M+1)], ColNums[1<<(MAX_M+1)];
char CMap[MAX_N][MAX_M];
int N, M, Scnt; #define LOOP0(i, n) for(int i=0; i<(n); i++)
#define LoopFrom(i, m, n) for(int i=(m); i<(n); i++)
#define Update(x, y) x=max(x, y)
#define In(B, A) (((B)&(A))==(B))
#define InterSect(x, y) ((x)&(y))
#define Join(A, x) ((A)|=(1<<(x))) int BCnt(int x)
{
int ans = 0;
while (x)
{
ans += (x & 1);
x >>= 1;
}
return ans;
} int CalColS(int *rowSs, int m)
{
int cnt = 0;
LOOP0(i, 1 << m)
if (!InterSect(i, i << 1) && (!InterSect(i, i << 2)))
{
rowSs[cnt++] = i;
ColNums[i] = BCnt(i);
}
return cnt;
} //S:state
int Proceed()
{
memset(DP, NINF, sizeof(DP));
LOOP0(j, Scnt)
{
int curS = ColSs[j];
if (In(curS, Map[0]))
{
DP[0][curS][0] = ColNums[curS];
}
}
LOOP0(j, Scnt)
{
int curS = ColSs[j];
if (In(curS, Map[1]))
{
LOOP0(k, Scnt)
{
int prevS = ColSs[k];
if (In(prevS, Map[0]) &&
!InterSect(prevS, curS))
{
DP[1][curS][prevS] = DP[0][prevS][0] + ColNums[curS];
}
}
}
}
LoopFrom(i,2,N)
{
memset(DP[i % 3], NINF, sizeof(DP[i % 3]));
LOOP0(j, Scnt)
{
int curS = ColSs[j];
if (In(curS, Map[i]))
{
LOOP0(k, Scnt)
{
int prevS = ColSs[k];
if (In(prevS,Map[i-1])&&
!InterSect(curS, prevS))
{
LOOP0(l, Scnt)
{
int grandS = ColSs[l];
if (In(grandS, Map[i - 2]) &&
!InterSect(curS, grandS) &&
!InterSect(prevS, grandS))
{
Update(DP[i % 3][curS][prevS],
DP[(i - 1) % 3][prevS][grandS] + ColNums[curS]);
}
}
}
}
}
}
}
int ans = 0;
LOOP0(j, Scnt)
{
int curS = ColSs[j];
if (In(curS, Map[N-1]))
{
LOOP0(k, Scnt)
{
int prevS = ColSs[k];
if (In(prevS, Map[N - 2]) && !InterSect(curS, prevS))
Update(ans, DP[(N-1)%3][curS][prevS]);
}
}
}
return ans;
} int main()
{
#ifdef _DEBUG
freopen("c:\\noi\\source\\input.txt", "r", stdin);
#endif
scanf("%d%d", &N, &M);
LOOP0(i, N)
{
scanf("\n%s", i+CMap);
LOOP0(j, M)
if (CMap[i][j] == 'P')
Join(Map[i], j);
}
Scnt = CalColS(ColSs, M);
printf("%d\n", Proceed());
return 0;
}