這道題和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 中的某個數,規定狀態之間的轉移方...