題解 P2016 戰略遊戲

2022-03-28 20:29:01 字數 2648 閱讀 6057

題目

解法跟 dalao @real_ljs 類似,但沒有用到遞迴

題目相當於需要求覆蓋這顆樹需要的最小點數

用 \(dp_\) 表示在這棵樹中,以 \(i\) 為根節點的子樹選/不選根節點的情況下,覆蓋這棵樹所有邊需要的最小點數

所以,當不選這個節點 \(i\) 時,則所有 以其子節點為根節點的子樹 都必選根節點

當選擇這個節點 \(i\) 時,它能連線到所有的子節點,所以 以其子節點為根節點的子樹 可以選則其根節點,也可以不選

歸納成方程組,可能更容易理解:

\(\beginson_i\neq \varnothing\\\ \\ \displaystyle dp_=\sum_dp_\\\ \\\displaystyle dp_=1+\sum_min(dp_,dp_)\end\)

其中,\(son_i\) 為 \(i\) 的子節點集合

顯然,邊界為

\(\beginson_i=\varnothing\\dp_=0\\dp_=1\end\)

如果定義 \(son_i=\varnothing\) 時 \(\displaystyle \sum_dp_=\sum_min(dp_,dp_)=0\)

遞推式及其邊界便能寫在一起了:

\(\begin\displaystyle dp_=0+\sum_dp_\\\ \\\displaystyle dp_=1+\sum_min(dp_,dp_)\end\)

答案即為 \(min(dp_,dp_)\)

很顯然,根據樹形dp的特點,我們要從葉子開始回溯回去,一般用dfs,或者說是遞迴,來輔助進行

我們來想想,這題的樹形dp只要滿足子節點的dp在父節點之前完成即可

那我們可以先從根節點開始bfs一下,把樹壓成一條鏈,然後再把bfs訪問的節點倒序完成dp即可

因為bfs過程中,子節點一定由父節點拓展而來,故子節點在bfs中一定在父節點之後訪問到

dp的時候我們倒敘訪問,就一定能保證子節點先完成dp,再由父節點完成dp

具體實現方法也不難,最後倒敘訪問實際上就是乙個棧,我們讓這個陣列在bfs的時候做佇列,dp的時候再當作棧即可

考慮一下時間複雜度:

讀入各個節點需要 \(o(n)\),讀入他們的子節點數也需要 \(o(n)\)

讀入子節點為 \(\displaystyle \sum_^n card(son_i)=n-1\) ,也為 \(o(n)\) 的

找根節點、bfs為 \(o(n)\)

dp的內層次為 \(o(\ \ card(son_i)\ \ )\)

dp的複雜度為 \(\displaystyle o(\ \ \sum_^n[1+card(son_i)]\ \ )=o(\ \ \displaystyle n+\sum_^ncard(son_i)\ \ )=o(n+n-1)\)

因此,dp的複雜度為 \(o(n)\)

很顯然,總複雜度也為 \(o(n)\)

簡直快得飛起

那本蒟蒻就放 我碼風極醜的 **了

#include#includeusing namespace std;

#define f(a,b,c,d) for(register int a=b,c=d;a<=c;a++)

#define g(a,b,c,d) for(register int a=b,c=d;a>=c;a--)

const int maxn=1510;

typedef int i32;

typedef unsigned int u32;

typedef long long int i64;

typedef unsigned long long int u64;

typedef i32 ar[maxn];

typedef i32 mt[maxn][maxn];

#define cls(s) memset(s,0,sizeof(s))

inline i32 min(int a,int b)

char output_ans[1<<20|1]=,*output_cur=output_ans;

inline void output()

inline void print(char c)

inline void print(i32 ans);

if(output_cur-output_ans+sprintf(s,"%d",ans)>>20) output();

output_cur+=sprintf(output_cur,"%d",ans);

}}using namespace io;

//條件反射般的讀入輸出優化

i32 d_n,d_cur;

ar ar_d_stk;

mt mt_d_son;

inline void pre();

f(i,1,i,d_n)

//讀入,並同時判定根節點

ar_d_stk[d_cur]=0;

while(b_notroot[ ar_d_stk[d_cur] ]) ar_d_stk[d_cur]++;

d_cur++;

//尋找根節點,放入佇列頭

i32 d_head=0;

while(d_head最後安利一下 本蒟蒻的部落格

P2016 戰略遊戲

樹形dp f u 0 表示 uf u 0 表示u f u 0 表示u 號節點不放士兵,以x為根的子樹需要的最少士兵數。f u 1 表示 uf u 1 表示u f u 1 表示u 號節點放士兵,以x為根的子樹需要的最少士兵數。由於我們定義的是將其完全覆蓋,則我們不需要考慮父親節點,為什麼?當我們已經回...

P2016 戰略遊戲

題面 題目背景 bob 喜歡玩電腦遊戲,特別是戰略遊戲。但是他經常無法找到快速玩過遊戲的辦法。現在他有個問題。題目描述 他要建立乙個古城堡,城堡中的路形成一棵無根樹。他要在這棵樹的結點上放置最少數目的士兵,使得這些士兵能瞭望到所有的路。注意,某個士兵在乙個結點上時,與該結點相連的所有邊將都可以被瞭望...

洛谷 P2016 戰略遊戲

題目描述 bob喜歡玩電腦遊戲,特別是戰略遊戲。但是他經常無法找到快速玩過遊戲的辦法。現在他有個問題。他要建立乙個古城堡,城堡中的路形成一棵樹。他要在這棵樹的結點上放置最少數目的士兵,使得這些士兵能瞭望到所有的路。注意,某個士兵在乙個結點上時,與該結點相連的所有邊將都可以被瞭望到。請你編一程式,給定...