有一棵點數為n的樹,樹邊有邊權。將m個點染成黑色,並將其他的點染成白色。會獲得黑點兩兩之間的距離和加上白點兩兩之間的距離和的收益。問收益最大值是多少。
輸入格式:
第一行兩個整數n、m。接下來n-1行,每行三個整數a、b、c,表示有一條樹邊連線a、b,長度為c。
輸出格式:
一行 乙個正整數,表示收益的最大值。
由題目我們可以考慮動態規劃,令f[i][j]表示以i為根節點的子樹上有j個結點為黑色可以得到的最大貢獻。這就成了乙個樹上揹包問題了。
對於f[i][j]這個狀態,除i以外的所有點可以被分為兩類,一類是在i 的子樹上的,另一類是在i的子樹外的。而在這兩個集合中各取乙個點相連一定會經過邊(i, father[i])我們可以算出在此時(i,father[i])產生的收益是(設子樹大小為size[i])子樹上的黑點個數(j)與子樹外的黑點個數(m - j)的乘積乘上這條邊的邊權(w[i])加上子樹上白點的個數(size[i] - j)乘以子樹外白點的個數(n - m - size[i] + j)再乘以邊權,這些貢獻是在加入了根節點以後才產生的新的貢獻,與子樹上黑白點如何分配無關。
然後,我們再來考慮子樹上黑白點的分配方法,由於之前的子樹分配對後面的子樹如何分配沒有影響,我們可以把子樹分為兩類,一類是已經確定大小的,一類是還沒有確定大小的,而這其中產生的貢獻即為當前討論的子樹上的黑點個數可以帶來的貢獻值,和這顆子樹以外的點可以帶來的貢獻值,而這顆子樹以外的點可以帶來的貢獻值,我們就只能先計算那些我們已經劃分好了的,而其餘的我們可以留在後面的子樹再繼續劃分(這是我覺得這道題最玄學的地方),所以我們狀態的轉移要像這樣寫:
void dp(intu)
}
#includeusingnamespace
std;
const
int max = 2005;
long
long
f[max][max], g[max];
int head[max], to[max << 1], next[max << 1], edgenum = 0, w[max << 1];
intn, m;
void add_edge(int
from, int to, int
c)
intsize[max];
void dp(int
u)
} intmain()
dp(1);
printf(
"%lld\n
", f[1
][m]);
return0;
}
題解 HAOI2015 樹上染色
題目描述 有一棵點數為 n 的樹,樹邊有邊權。給你乙個在 0 n之內的正整數 kkk 你要在這棵樹中選擇 k 個點,將其染成黑色,並將其他 的 n k個點染成白色。將所有點染色後,你會獲得黑點兩兩之間的距離加上白點兩兩之間的距離的和的受益。問受益最大值是多少。輸入格式 第一行包含兩個整數 n,k。第...
樹DP 樹上染色
haoi2015 樹上染色 時間限制 1 s 記憶體限制 256 mb 題目描述 有一棵點數為n的樹,樹邊有邊權。給你乙個在0 n之內的正整數k,你要在這棵樹中選擇k個點,將其染成黑色,並將其他的n k個點染成白色。將所有點染色後,你會獲得黑點兩兩之間的距離加上白點兩兩之間距離的和的收益。問收益最大...
樹DP 樹上染色
haoi2015 樹上染色 時間限制 1 s 記憶體限制 256 mb 題目描述 有一棵點數為n的樹,樹邊有邊權。給你乙個在0 n之內的正整數k,你要在這棵樹中選擇k個點,將其染成黑色,並將其他的n k個點染成白色。將所有點染色後,你會獲得黑點兩兩之間的距離加上白點兩兩之間距離的和的收益。問收益最大...