1. 程式人生 > >洛谷P2704 [NOI2001]炮兵陣地 [狀壓DP]

洛谷P2704 [NOI2001]炮兵陣地 [狀壓DP]

ref font radi 空格 一行 包含 tar syn clas

  題目傳送門

炮兵陣地

題目描述

司令部的將軍們打算在N*M的網格地圖上部署他們的炮兵部隊。一個N*M的地圖由N行M列組成,地圖的每一格可能是山地(用“H” 表示),也可能是平原(用“P”表示),如下圖。在每一格平原地形上最多可以布置一支炮兵部隊(山地上不能夠部署炮兵部隊);一支炮兵部隊在地圖上的攻擊範圍如圖中黑色區域所示:

技術分享圖片

如果在地圖中的灰色所標識的平原上部署一支炮兵部隊,則圖中的黑色的網格表示它能夠攻擊到的區域:沿橫向左右各兩格,沿縱向上下各兩格。圖上其它白色網格均攻擊不到。從圖上可見炮兵的攻擊範圍不受地形的影響。 現在,將軍們規劃如何部署炮兵部隊,在防止誤傷的前提下(保證任何兩支炮兵部隊之間不能互相攻擊,即任何一支炮兵部隊都不在其他支炮兵部隊的攻擊範圍內),在整個地圖區域內最多能夠擺放多少我軍的炮兵部隊。

輸入輸出格式

輸入格式:

第一行包含兩個由空格分割開的正整數,分別表示N和M;

接下來的N行,每一行含有連續的M個字符(‘P’或者‘H’),中間沒有空格。按順序表示地圖中每一行的數據。N≤100;M≤10。

輸出格式:

僅一行,包含一個整數K,表示最多能擺放的炮兵部隊的數量。

輸入輸出樣例

輸入樣例#1:

5 4

PHPP

PPHH

PPPP

PHPP

PHHP

輸出樣例#1:

6


  分析:

  好吧,這是一道狀壓DP模板,但是反正考場上沒想到。。。

  只要枚舉本行的所有情況和上面兩行的所有情況,然後判斷一下在轉移即可。

  Code:

  

//It is made by HolseLee on 21th July 2018
//Luogu.org P2704
#include<bits/stdc++.h>
using namespace std;
int n,m,ans,a[105],dp[(1<<10)][(1<<10)][3],sum[(1<<10)];
inline int get(int x)
{
    int ret=0;
    while(x){if(x&1
)ret++;x>>=1;} return ret; } int main() { freopen("cannon.in","r",stdin); freopen("cannon.out","w",stdout); ios::sync_with_stdio(false); cin>>n>>m; char op; for(int i=0;i<n;i++) for(int j=0;j<m;j++){ cin>>op;a[i]<<=1; a[i]+=(op==H?1:0);} for(int S=0;S<(1<<m);S++) sum[S]=get(S); for(int S=0;S<(1<<m);S++) if(!((S&a[0])||(S&(S<<1))||(S&(S<<2)))) dp[0][S][0]=sum[S]; for(int L=0;L<(1<<m);L++) for(int S=0;S<(1<<m);S++) if(!((L&S)||(L&a[0])||(S&a[1])||(L&(L<<1))||(L&(L<<2))||(S&(S<<1))||(S&(S<<2)))) dp[L][S][1]=sum[L]+sum[S]; for(int i=2;i<n;i++) for(int L=0;L<(1<<m);L++){ if((L&a[i-1])||(L&(L<<1))||(L&(L<<2)))continue; for(int S=0;S<(1<<m);S++){ if((S&L)||(S&a[i])||(S&(S<<1))||(S&(S<<2)))continue; for(int FL=0;FL<(1<<m);FL++){ if((FL&L)||(FL&S)||(FL&a[i-2])||(FL&(FL<<1))||(FL&(FL<<2)))continue; dp[L][S][i%3]=max(dp[L][S][i%3],dp[FL][L][(i-1)%3]+sum[S]); } } } for(int L=0;L<(1<<m);L++) for(int S=0;S<(1<<m);S++) ans=max(ans,dp[L][S][(n-1)%3]); cout<<ans<<"\n"; return 0; }

洛谷P2704 [NOI2001]炮兵陣地 [狀壓DP]