JZOJ 1768【NOI2001】炮兵阵地

前言

此题出现在2016.1.24日的比赛中。当时最高分是HJY,拿了50分。而我在比赛中因为卡在其他题上所以只水了样例,竟然过了一个点。

题目描述

 司令部的将军们打算在N*M(N≤100;M≤10)的网格地图上部署他们的炮兵部队。一个N*M的地图由N行M列组成,地图的每一格可能是山地(用“H” 表示),也可能是平原(用“P”表示),如下图。在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队);一支炮兵部队在地图上的攻击范围如图中黑色区域所示: 如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。图上其它白色网格均攻击不到。从图上可见炮兵的攻击范围不受地形的影响。   现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队。

分析

此题状压DP模型显然,因为M最大才10,且每个点都只有放与不放两种情况,所以我们可以将每一行的状态压缩成一个二进制数。
f[i][j][k] 表示当前做到第i行,第i-1行的状态为j,第i行的状态为k最多能放置多少炮兵。设第i+1行的状态为l,则转移方程很显然是 f[i+1][k][l]=max(f[i][j][k]+num(l));
其中 num(l) 表示l的二进制位中1的个数。
这样子我们粗略估计一下时间复杂度是 O(N2M2M2MM) ,最大则有 O(1073741824000) ,蒻菜被吓哭QAQ。
BUT,PAY ATTENTION:

可行的状态少之又少,因为要满足每个炮兵不在其他的攻击范围内,所以相邻的两个1中间至少要隔着两个0。即只要出现….101…..或….11……的状态都是不合法的。同时因为上下两格也会影响,所以当j,k,l三个状态同一位置出现了1时,也是不合法的。再加上题目给定山坡不能放置的限制,所以此题并不会超时。而要判断这些情况,请读者自己用位运算乱搞。

于是乎,这道题就解决了
你以为这样就解决了?当你满心欢喜调对样例交上去时看着自己MLE 0是什么滋味呢?
因为f[i]只会由f[i-1]转移而来,所以可以开滚动数组。
于是乎,这道题就解决了。
贴代码,不用谢:

#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int N=110,M=11;
int n,m,map[N],f[2][1<<M][1<<M];
int num(int x)
{
    int tot=0;
    fo(i,0,m-1)
        if(x&(1<<i)) tot++;
    return tot;
}
int main()
{
    char ch;
    scanf("%d %d\n",&n,&m);
    fo(i,1,n)
    {
        fo(j,1,m)
        {
            scanf("%c",&ch);
            map[i]+=(ch=='P')*(1<<(j-1));
        }
        scanf("\n");
    }
    int p=0,ans=0;
    fo(i,0,n-1)
    {
      fo(j,0,(1<<m)-1)
        if(!(j&((j<<1)|(j<<2))&((1<<m)-1)) && !(j&((j>>1)|(j>>2))&((1<<m)-1)) && !(j&(~map[i-1])))
          fo(k,0,(1<<m)-1)
            if(!(k&((k<<1)|(k<<2))&((1<<m)-1)) && !(k&((k>>1)|(k>>2))&((1<<m)-1)) && !(k&(~map[i])) && !(j&k))
              fo(l,0,(1<<m)-1)
                if(!(l&((l<<1)|(l<<2))&((1<<m)-1)) && !(l&((l>>1)|(l>>2))&((1<<m)-1)) && !(l&(~map[i+1])) && !((j|k)&l))
                {
                    f[p^1][k][l]=max(f[p^1][k][l],f[p][j][k]+num(l));
                    if(i==n-1) ans=max(ans,f[p^1][k][l]);
                }
      p^=1;
    }
    printf("%d",ans);
}

你可能感兴趣的:(题解,状压dp)