1. 程式人生 > >洛谷1122最大最大子樹和

洛谷1122最大最大子樹和

題目連結: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));//輸出錯誤答案 return
0; }

究其原因,樣例就能是出來,因為任選一節點作為根節點,答案不一定最優(樣例中以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;
}