題目描述
有一棵點數為 n 的樹,樹邊有邊權。給你乙個在 0∼n之內的正整數 kkk ,你要在這棵樹中選擇 k 個點,將其染成黑色,並將其他 的 n−k個點染成白色。將所有點染色後,你會獲得黑點兩兩之間的距離加上白點兩兩之間的距離的和的受益。問受益最大值是多少。
輸入格式
第一行包含兩個整數 n,k。
第二到 n 行每行三個正整數 fr,to,dis,表示該樹中存在一條長度為 dis 的邊 (fr,to)。輸入保證所有點之間是聯通的。
輸出格式
輸出乙個正整數,表示收益的最大值。
今日在洛谷無意中刷到的題目,一開始設定dp[i][j]表示i子樹內有j個黑色點的最大價值,後來就發現轉移不了,於是看了大神的題解,有所啟發,受益匪淺。
對於這種樹上選點計算點對貢獻,往往不用考慮點的具體位置,只需要考慮邊的貢獻,因為邊的貢獻只需要知道左右兩側對應顏色的數量和邊的權值即可計算。
設定dp[i][j]表示i子樹內有j個黑色點所帶來的貢獻,考慮乙個i的乙個子樹y有j』個黑色點,這條邊的貢獻為 w * 邊兩側的黑色點乘積+w * 邊兩側的白色點乘積。樹形揹包轉移即可。
#include
#define ll long long
#define rint register int
using
namespace std;
const
int n=
2005
;inline
intread()
int tot=
0,first[n]
,n,k;
struct fuk
a[n<<1]
;inline
void
add(
int x,
int to,
int v)
ll dp[n]
[n];
//子樹i裡面選了j個黑點的貢獻
int sz[n]
;inline
void
dfs(
int x,
int fa)
for(
int i=first[x]
;i;i=a[i]
.next)}}
intmain()
dfs(1,
0);printf
("%lld\n"
,dp[1]
[k])
;return0;
}
啟示:樹上選點問題往往不用考慮點的具體位置,考慮邊的貢獻使得樹形dp的轉移方便。以前模擬賽做過很多類似思想的題目,算是很不錯的題目。 HAOI2015 樹上染色
有一棵點數為 n 的樹,樹邊有邊權。給你乙個在 0 n 之內的正整數k,你要在這棵樹中選擇 k 個點,將其染成黑色,並將其他的 n k個點染成白色。將所有點染色後,你會獲得黑點兩兩之間的距離加上白點兩兩之間距離的和的收益。問收益最大值是多少。輸入第一行兩個整數 n,k。接下來 n 1 行每行三個正整...
HAOI2015 樹上染色
考慮子樹當中所有邊的貢獻即可。然後就能簡單做樹上揹包了。但是要注意列舉的順序,應該從大到小更新,否則某個狀態會多次被加。如果不想考慮列舉順序,那麼直接dp的時候用乙個臨時陣列記錄。ac pragma gcc optimize ofast funroll all loops include defin...
HAOI2015 樹上染色
嘟嘟嘟 首先這一眼看出來,要樹形dp。然後發現狀態不好設,剛開始我想的是dp i j 表示以 i 為根的子樹,選了 j 個黑點的最大價值。結果就不會轉移了。轉移的時候想考慮 這一條邊的貢獻,但是發現這個狀態的轉移所涉及的不只是這一條邊,還有子樹中的邊,於是就徹底gg了。還是看了題解。題解也是考慮貢獻...