題解 P2704 炮兵陣地

2021-10-02 15:29:53 字數 2283 閱讀 4831

這道題和p1879 [usaco06nov]玉公尺田corn fields有類似的地方,但這道題可以看為那道題的公升級版,所以我建議沒做過玉公尺田的可以先做一下玉公尺田p1896 [scoi2005]互不侵犯king

解此題的關鍵在於要知道第i行的狀態是由前兩行的狀態決定的,所以要預處理出第一行和第二行的所有狀態,然後從第三行(因為第一二行已處理)開始列舉,同時列舉第當前行的前一行和上上行。

for

(int i=

0;i<(1

<;i++)if

(!(i&(i<<2)

)&&!(i&

(i<<1)

))}

f[i][l][j]的意思是第i行的j狀態是由上一行的l狀態得出來的。

首先先不論正確性,交上去會wa5個點,實際值都比答案小;其次這道題和玉公尺田不同的是此題求的是最多炮兵數(是最優性問題求解),而玉公尺田問的是總方案數,這是有很大區別的;然後既然是是最優性問題求解,那麼肯定要從前兩行的所有狀態中求出使當前行狀態最優的狀態,滿足階段最優和無後效性原則。

最後從最後一行(即最終狀態)中找出最優狀態,這就是解。

#include

#include

#include

#include

using

namespace std;

long

long f[

105]

[500][

500]

,sit[

5000

],n,k=

0,ans=-1

,m,map[

5000

],sum[

5000];

intmain()

}for

(int i=

0;i<(1

<;i++

)//處理炮兵放置方案(情況) if(

!(i&

(i<<2)

)&&!(i&

(i<<1)

))}for

(int i=

1;i<=k;i++)if

(!(sit[i]

&map[1]

))f[1]

[1][i]

=sum[i]

;//處理第一行的狀態

//下面乙個for是處理第二行的狀態

for(

int i=

1;i<=k;i++

)//列舉第二行所有炮兵放置情況 if(

!(sit[i]

&map[2]

))for(

int j=

1;j<=k;j++

)//列舉第一行……

for(

int i=

3;i<=n;i++

)for

(int j=

1;j<=k;j++

)//列舉當前行…… if(

!(map[i]

&sit[j]))

for(

int l=

1;l<=k;l++

)//列舉上一行…… if(

!(sit[j]

&sit[l])&&

!(map[i-1]

&sit[l]))

for(

int p=

1;p<=k;p++

)//列舉上上行…… if(

!(sit[p]

&sit[l])&&

!(map[i-2]

&sit[p])&&

!(sit[p]

&sit[j]))

f[i]

[l][j]

=max

(f[i]

[l][j]

,f[i-1]

[p][l]

+sum[j]);

for(

int i=

1;i<=k;i++

)for

(int j=

1;j<=k;j++

)ans=

max(f[n]

[j][i]

,ans)

;//處理答案

printf

("%lld"

,ans)

;}

洛谷P2704 炮兵陣地

本題過於經典.對於這種網格狀壓dp,套路一波刷表法dfs轉移就沒了。三進製狀壓,0表示當前,上乙個都沒有。1表示當前無,上乙個有。2表示當前有。轉移的條件就是上一行為0,當前不是山地,且左邊兩個都不是2。注意有個坑點,全部轉移會超時。因為本題有很多廢狀態 山地 初始化 1然後判斷是否轉移即可。1 i...

P2704 NOI2001 炮兵陣地 題解

題目描述就不贅述了。解題思路 這道題的判斷合法的方式比較常見,簡單位運算即可,關鍵是空間和狀態轉移方程。預處理 可以證明有效的方案數不會超過200,這個數字只是我估算的上界,不嚴格,預處理出每一行的方案即可。狀態轉移方程 每一行由上一行遞推得到,f i j k 表示前i行,第i行狀態為j,第i 1行...

P2704 NOI2001 炮兵陣地

炮兵陣地 這道題之前已經討論過二進位制狀壓的方法了,通過 滾動陣列 預處理 的方法可以達到極高效率。但是這裡給出一種更具有普適性的方法。對於和之前多個階段有關聯的 dp,我們可以使用多進製狀壓來處理。具體方法 假設進行 k 進製狀壓,將每一位的狀態表示為 0 k 1 中的某個數,規定狀態之間的轉移方...