洛谷1122最大最大子樹和
阿新 • • 發佈:2018-11-11
題目連結:https://www.luogu.org/problemnew/show/P1122
這題嘛 … 很明顯的樹形dp,但也有很多要注意的小點。
一開始題目打在了紙上,樹的結構在第二面,然後當成了區間dp,嗯 … 程式碼就不貼了
於是我又打起了程式碼。
首先轉移方程好想:預設選v節點,再遞迴,將返回值與0進行比較,看有沒有在美麗指數上有好的幫助,加起來後返回。
再就是存樹,我是用vector當圖存的,應是無向邊,所以應在遞迴時把父親記下來,轉移時就不搜父節點,不然會死迴圈。
於是另一份交了上去,結果依然沒過
附錯誤程式碼:
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<vector> using namespace std; const int N=16005; vector<int>E[N];//stl存圖 int n,w[N]; int dp(int v,int fa){//給父節點以免死迴圈 int ans=w[v]; for(int i=0;i<E[v].size();i++) if(E[v][i]!=fa) ans+=max(0,dp(E[v][i],v)); return ans; } int main(){ scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",w+i); for(int i=1,x,y;i<=n-1;i++){ scanf("%d%d",&x,&y); E[x].push_back(y);//不知道那個為父節點 E[y].push_back(x);//所以當無向圖 } printf("%d\n",dp(1,0));//輸出錯誤答案 return0; }
究其原因,樣例就能是出來,因為任選一節點作為根節點,答案不一定最優(樣例中以1為根節點,答案就不優,為2)。
解決辦法很簡單,以每個節點為根節點跑一邊dp,取最優值。再交上去後發現超時了幾個點,說明不能這麼暴力。
附70分程式碼:
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<vector> using namespace std; const int N=16005; vector<int>E[N]; int n,w[N]; int dp(int v,int fa){//給父節點以免死迴圈 int ans=w[v]; for(int i=0;i<E[v].size();i++) if(E[v][i]!=fa) ans+=max(0,dp(E[v][i],v)); return ans; } int main(){ scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",w+i); for(int i=1,x,y;i<=n-1;i++){ scanf("%d%d",&x,&y); E[x].push_back(y);//不知道哪個為父節點 E[y].push_back(x);//所以當無向圖 } int ans=0; for(int i=1;i<=n;i++) ans=max(ans,dp(i,0)); printf("%d\n",ans); return 0; }
冥思苦想後,我終於先到了正解方法
在dp時是預設選根節點的,但選根節點不一定最優,最優情況可能是去掉某些子樹,也可能還去掉根節點,用一下記憶化搜尋,把結果遍歷一遍,這樣就可以考慮到所有情況,時間複雜度也很優。
附AC程式碼:
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<vector> using namespace std; const int N=16005; vector<int>E[N];//vector存圖(樹)仿領接表 int n,w[N],f[N]; int dp(int v,int fa){//記住父親節點,避免死迴圈 int ans=w[v]; for(int i=0;i<E[v].size();i++) if(E[v][i]!=fa) ans+=max(0,dp(E[v][i],v)); return f[v]=ans; } int main(){ scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",w+i); for(int i=1,x,y;i<=n-1;i++){ scanf("%d%d",&x,&y); E[x].push_back(y);//此時不確定父節點,當無向圖存 E[y].push_back(x); } int ans=0; dp(1,0); for(int i=1;i<=n;i++) ans=max(ans,f[i]);//將記憶化搜尋的結果逐一比較 printf("%d\n",ans); return 0; }