[NOI2001] 炮兵阵地
题目
Description
司令部的将军们打算在 N×MN×M 的网格地图上部署他们的炮兵部队。
一个 N×MN×M 的地图由 NN 行 MM 列组成,地图的每一格可能是山地(用 HH 表示),也可能是平原(用 PP 表示),如下图。
在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队);一支炮兵部队在地图上的攻击范围如图中黑色区域所示:

如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。
图上其它白色网格均攻击不到。从图上可见炮兵的攻击范围不受地形的影响。
现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队。
1≤N≤100,1≤M≤10
Input
第一行包含两个由空格分割开的正整数,分别表示 NN 和 MM。
接下来的 NN 行,每一行含有连续的 MM 个字符,按顺序表示地图中每一行的数据。
Output
一行一个整数,表示最多能摆放的炮兵部队的数量。
Sample Input
5 4 PHPP PPHH PPPP PHPP PHHP
Sample Output
6
思路
一道状压$dp$的题;
枚举当前行和上两行的状态,并保证炮兵不能互相攻击即可;
状态转移方程就为
$dp[i][x][y]=max(dp[i][x][y],dp[i-1][y][z]+sum);$
$x$为第$i$行状态,$y$为第$i-1$行的状态,$z$为第$i-2$行状态,$sum$为第$i$行炮兵个数;
不过要注意的是$m$最大范围是$10$,三个枚举状态的循环会超时;
所以需要提前把满足条件的行预处理到一个数组,再枚举数组即可;
这样每个循环次数可以降到$1<<6$次以下,是不会超时的;
代码
#include<bits/stdc++.h> typedef long long ll; using namespace std; ll n,m; ll tot; ll a[1<<10],c[101][11]; ll dp[101][1<<6][1<<6]; int main() { scanf("%lld%lld",&n,&m); for(ll i=1;i<=n;i++) for(ll j=1;j<=m;j++) { char cc; cin>>cc; if(cc=='P') c[i][j]=1; } for(ll x=0;x<=(1<<m)-1;x++) { if(x&(x<<1)||x&(x<<2)) continue; a[++tot]=x;//预处理满足条件的行 } for(ll i=1;i<=n;i++) for(ll x=1;x<=tot;x++) { ll flag=0; for(ll j=1;j<=m;j++) if(c[i][j]==0&&a[x]&(1<<(j-1)))//如果是山地,就不能放炮兵 { flag=1; break; } if(flag) continue; ll sum=0; for(ll j=1;j<=m;j++) if(a[x]&(1<<(j-1))) sum++;//记录当前行炮兵数量 for(ll y=1;y<=tot;y++) for(ll z=1;z<=tot;z++) { if(a[x]&a[y]||a[x]&a[z]) continue;//保证与上两行互不攻击 dp[i][x][y]=max(dp[i][x][y],dp[i-1][y][z]+sum);//求最大数量 } } ll ans=0; for(ll x=1;x<=tot;x++) for(ll y=1;y<=tot;y++) ans=max(ans,dp[n][x][y]); printf("%lld",ans); return 0; }

浙公网安备 33010602011771号