给一棵 n n n个节点的树, 节点编号为 1 1 1~ n n n, 每条边都有一个花费值。
有 k k k个机器人从 s s s点出发, 问让机器人遍历所有边,最少花费值多少?
指定节点出发,那么我们需不需要考虑从当前节点向子节点和祖先节点两种情况呢?
不需要,虽然是指定 s s s 出发,但是因为是无根树,所以我们依然可以只考虑子节点的情况,只需要把 s s s 当作树根即可
想到这里,我们应该已经意识到这是一道树形dp的问题,则子问题的划分显而易见:
从以 u u u 为根节点的子树出发,派出 k k k 个机器人,遍历完子树所需的最小代价
如果把 k k k 个机器人派出,那么他们还返不返回呢?
显然,很可能要返回
那么这里又出现了一个很重要的问题:回边,这也是这道题的难点
如果只有一个机器人,且最后需要返回 s s s
一个很显然的结论是:每一条边显然要被走过两遍,总代价设为 s u m sum sum 则 s u m = ∑ w ( u , v ) ∣ u , v 是树上相邻的节点, w ( u , v ) 是边权 sum=\sum w(u,v) |u,v是树上相邻的节点,w(u,v)是边权 sum=∑w(u,v)∣u,v是树上相邻的节点,w(u,v)是边权
如果只有一个机器人,最后不需要返回 s s s
那么我们如果想要代价最小,那么只遍历一次的边就要尽量的多
A n s = s u m − ∑ w ( u , v ) ∣ w ( u , v ) 只遍历一遍 Ans = sum - \sum w(u,v) | w(u,v)只遍历一遍 Ans=sum−∑w(u,v)∣w(u,v)只遍历一遍
其实也就是减去一条从 s s s 到叶子节点的最长路径
假设一条边 w ( u , v ) w(u,v) w(u,v)被 k k k个机器人总共遍历了 x x x遍,则最后的总代价会减少 2 × w ( u , v ) − x × w ( u , v ) 2 \times w(u,v) - x \times w(u,v) 2×w(u,v)−x×w(u,v) 我们要这个值最大
换一种方法理解这个式子,如果我们派出 k k k 个机器人,那么最后会返回几个呢?
很显然是 1 1 1 个,为什么呢?
如果我们让 y y y 个机器人返回,那么 w ( u , v ) w(u,v) w(u,v) 就会被走 y × w ( u , v ) y \times w(u,v) y×w(u,v)次,且子树内的边仍然是不变的
所以很显然更劣
那么 k 个 走一遍,最后1个返回,也就是 ( k + 1 ) × w ( u , v ) = x × w ( u , v ) (k + 1) \times w(u,v) = x \times w(u,v) (k+1)×w(u,v)=x×w(u,v) (因为总共是被遍历 x x x遍,又只有 1 1 1个遍历了两边所以 x = k + 1 x = k + 1 x=k+1
因此我们的 d p dp dp状态也就设好了,设 d p [ i ] [ j ] dp[i][j] dp[i][j] 表示以 i i i 为根节点的子树内派出 j j j 个机器人,所减去的代价最大为多少
这样我们所要求的就是只遍历一遍的边:成功避免了回边问题
#include
using namespace std;
const int N = 1e4,MAXK = 10;
int n,s,k,sum;
vector<pair<int,int> > G[N + 5];
int dp[N + 5][MAXK + 5];
void dfs(int u,int f) {
for(auto e: G[u]) {
int v = e.first,w = e.second;
if (v == f) continue;
dfs(v,u);
for(int i = k;i >= 1;i --) {
for(int j = 1;j <= i;j ++) {
dp[u][i] = max(dp[u][i],dp[u][i - j] + dp[v][j] + 2 * w - j * w);
}
}
}
}
int main() {
scanf("%d%d%d",&n,&s,&k);
for(int i = 1,x,y,w;i < n;i++) {
scanf("%d%d%d",&x,&y,&w);
G[x].push_back({y,w});
G[y].push_back({x,w});
sum += w;
}
dfs(s,0);
printf("%d",2 * sum - dp[s][k]);
return 0;
}
/*
一个机器人遍历整个树再返回根节点,花费 2 * sum_{wi}
如果不返回,则花费 2 * sum_{wi} - max{路径长度}
多个机器人,尽量让 max{只走一次的路径长度} 的和最长
*/