1. 程式人生 > 實用技巧 >「Solution」CodeForces 633F The Chocolate Spree

「Solution」CodeForces 633F The Chocolate Spree

本題是華一高Ks 2020.11.26 T4 Attack

\(Description\)

給出一棵有\(n\)個節點的樹,每個節點有一個權值\(a_i\),求出不相交的兩條鏈的最大權值和。

\(2 \leq n \leq 10^5\)

\(1 \leq a_i \leq 10^9\)

\(Solution\)

一道樹形DP好題,難點在於狀態的轉移,即兩條鏈的形態。

不要怕多設狀態,重點在於把所有情況都描述出來。

\(DP_{now,0}: 最大兩條鏈之和\)
\(DP_{now,1}: 最大一條鏈\)
\(DP_{now,2}: 到葉子節點的鏈+一條與之不相交的鏈最大值\)
\(DP_{now,3}: 兒子節點DP_{x,1}最大值\)


\(DP_{now,4}: 到葉子節點的最大鏈\)

對於每種轉移,程式碼裡有解釋,建議在草稿紙上畫圖,對理解很有幫助。

\(AC \space Code\)

#include<iostream>
#include<fstream>
#include<sstream>
#include<cstdio>
#include<vector>
#include<set>
#include<queue>
#include<string>
#include<cstring>
#include<map>
#include<list>
#include<deque>
#include<cctype>
#include<climits>
#include<cmath>
#include<ctime>
#include<algorithm>
#define File(name) freopen(name".in", "r", stdin); freopen(name".out", "w", stdout);
#define Int inline int
#define Void inline void
#define Bool inline bool
#define DB inline double
#define LL inline long long
#define ri register int
#define re register
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
ll n, u, v, a[100005], dp[100005][5];
vector<int> g[100005];
LL read(){
	ll n = 0; int f = 1; char ch = getchar();
	while('0' > ch || ch > '9'){
		if(ch == '-') f *= -1;
		ch = getchar();
	}
	while('0' <= ch && ch <= '9'){
		n = (n << 1) + (n << 3) + ch-'0';
		ch = getchar();
	}
	return f * n;
}
Void write(ll x){
	if(x/10) write(x/10);
	putchar(x%10+'0');
}
Void input() {}
template<typename Type, typename... Types>
Void input(Type& arg, Types&... args){
    arg = read();
    input(args...);
}
Void dfs(int now, int fa){
    // 初始化
    dp[now][0] = dp[now][1] = dp[now][2] = dp[now][4] = a[now];
    dp[now][3] = 0;

    for(auto x: g[now]){
        if(x == fa) continue;
        dfs(x, now);

        /* ***************************************** *
        * dp[][0]: 最大兩條鏈之和
        * dp[][1]: 最大一條鏈
        * dp[][2]: 到葉子節點的鏈+一條與之不相交的鏈最大值
        * dp[][3]: 兒子節點dp[][1]最大值
        * dp[][4]: 到葉子節點的最大鏈
        * ***************************************** */

        dp[now][0] = max(dp[now][0], dp[now][1] + dp[x][1]);           // 子樹內最大一條鏈+子節點子樹內最大一條鏈
        dp[now][0] = max(dp[now][0], dp[x][0]);                        // 從子節點直接轉移
        dp[now][0] = max(dp[now][0], dp[x][2] + dp[now][4]);           // 子節點子樹內到葉子節點的鏈 與 子樹內到葉子節點的鏈構成一條穿過當前節點的鏈+一條與之不相交的鏈
        dp[now][0] = max(dp[now][0], dp[now][2] + dp[x][4]);           // 與上面的轉移類似

        dp[now][1] = max(dp[now][1], dp[x][1]);                        // 從子節點直接轉移
        dp[now][1] = max(dp[now][1], dp[now][4] + dp[x][4]);           // 子節點子樹內到葉子節點的鏈 與 子樹內到葉子節點的鏈構成一條穿過當前節點的鏈

        dp[now][2] = max(dp[now][2], dp[x][2] + a[now]);               // 從子節點直接轉移,將子節點子樹內那條到達葉子結點的鏈延伸到當前節點
        dp[now][2] = max(dp[now][2], dp[x][4] + dp[now][3] + a[now]);  // 與上面的轉移類似
        dp[now][2] = max(dp[now][2], dp[now][4] + dp[x][1]);           // 按照dp[][2]的定義直接轉移

        dp[now][3] = max(dp[now][3], dp[x][1]);                        // 按照dp[][3]的定義直接轉移

        dp[now][4] = max(dp[now][4], dp[x][4] + a[now]);               // 按照dp[][4]的定義直接轉移,將最大鏈延伸到當前節點
    }
}
int main(){
    input(n);
    for(ri i = 1; i <= n; i++) input(a[i]);
    for(ri i = 1; i < n; i++){
        input(u, v);
        g[u].push_back(v);
        g[v].push_back(u);
    }
    dfs(1, 0);
    write(dp[1][0]);
	return 0;
}
  • 最優解\(Rank15\)
  • 1.39s
  • 11.84MB
  • 2.40KB