有一棵點數為 n 的樹,樹邊有邊權。給你乙個在 0~ n 之內的正整數 k ,你要在這棵樹中選擇 k個點,將其染成黑色,並將其他 的n-k個點染成白色 。 將所有點染色後,你會獲得黑點兩兩之間的距離加上白點兩兩之間的距離的和的受益。問受益最大值是多少。
第一行包含兩個整數 n, k 。接下來 n-1 行每行三個正整數 fr, to, dis , 表示該樹中存在一條長度為 dis 的邊 (fr, to) 。輸入保證所有點之間是聯通的。
輸出乙個正整數,表示收益的最大值。
3 1對於 100% 的資料, 0<=k<=n <=2000 一開始設計的狀態是f[i][j]表示以i為根的子樹選j個黑點的最大值1 2 1
1 3 2
但是這樣難以轉移狀態
正解是用f[i][j]表示以i為根的子樹上有j個黑點時對最終答案的最大貢獻
聽起來好像挺像但實際上完全不一樣
我們只需要計算當前子樹內的所有黑點到父親節點的距離乘以子樹外的黑點個數
因為這些點不論子樹外的黑點選在**都會對最終答案產生這些貢獻
子樹內的黑點對答案造成的貢獻已經在子樹內兩兩黑點的lca處合併成完整的貢獻了
白點也是一樣的 ==
這樣狀態就可以轉移了
直接在樹上做揹包就可以辣 ><
#include#include#include# define ll long long
const ll inf = 1e16 ;
const int m = 2005 ;
inline ll read()
while(c>='0'&&c<='9')
return x*w ;
}int n , m ;
struct eedge[m<<1];
int hea[m] , num , size[m] ;
ll f[m][m] ;
inline void add_edge(int from , int to , ll dis)
void dfs(int u , int father)
}}int main()
for(int i = 1 ; i <= n ; i ++)
for(int j = 1 ; j <= n ; j ++)
f[i][j] = -inf ;
dfs(1 , 1) ;
printf("%lld\n",f[1][m]) ;
return 0 ;
}
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了。還是看了題解。題解也是考慮貢獻...