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