試題 算法提高 金屬採集
資源限制
時間限制:1.0s 內存限制:256.0MB
問題描述
人類在火星上發現了一種新的金屬!這些金屬分佈在一些奇怪的地方,不妨叫它節點好了。一些節點之間有道路相連,所有的節點和道路形成了一棵樹。一共有 n 個節點,這些節點被編號爲 1~n 。人類將 k 個機器人送上了火星,目的是採集這些金屬。這些機器人都被送到了一個指定的着落點, S 號節點。每個機器人在着落之後,必須沿着道路行走。當機器人到達一個節點時,它會採集這個節點蘊藏的所有金屬礦。當機器人完成自己的任務之後,可以從任意一個節點返回地球。當然,回到地球的機器人就無法再到火星去了。我們已經提前測量出了每條道路的信息,包括它的兩個端點 x 和 y,以及通過這條道路需要花費的能量 w 。我們想花費盡量少的能量採集所有節點的金屬,這個任務就交給你了。
輸入格式
第一行包含三個整數 n, S 和 k ,分別代表節點個數、着落點編號,和機器人個數。
接下來一共 n-1 行,每行描述一條道路。一行含有三個整數 x, y 和 w ,代表在 x 號節點和 y 號節點之間有一條道路,通過需要花費 w 個單位的能量。所有道路都可以雙向通行。
輸出格式
輸出一個整數,代表採集所有節點的金屬所需要的最少能量。
樣例輸入
6 1 3
1 2 1
2 3 1
2 4 1000
2 5 1000
1 6 1000
樣例輸出
3004
樣例說明
所有機器人在 1 號節點着陸。
第一個機器人的行走路徑爲 1->6 ,在 6 號節點返回地球,花費能量爲1000。
第二個機器人的行走路徑爲 1->2->3->2->4 ,在 4 號節點返回地球,花費能量爲1003。
第一個機器人的行走路徑爲 1->2->5 ,在 5 號節點返回地球,花費能量爲1001。
數據規模與約定
本題有10個測試點。
對於測試點 1~2 , n <= 10 , k <= 5 。
對於測試點 3 , n <= 100000 , k = 1 。
對於測試點 4 , n <= 1000 , k = 2 。
對於測試點 5~6 , n <= 1000 , k <= 10 。
對於測試點 7~10 , n <= 100000 , k <= 10 。
道路的能量 w 均爲不超過 1000 的正整數。
實現代碼
#include<iostream>
#include<vector>
#include<cstring>
#include<algorithm>
using namespace std;
typedef pair<int, int> Edge;
const int maxn = 1e5 + 5;
vector<Edge> edge[maxn];
int vis[maxn], dp[maxn][11], n, s, k;
void dfs(int u) {
vis[u] = 1;
for (int i = 0; i < edge[u].size(); i++) {
int v = edge[u][i].first, w = edge[u][i].second;
if (vis[v]) continue;
dfs(v);
// 在 u / 父 樹中放j個機器人,注意要從k開始,如果從0開始會對大於0的產生影響,爲了避免影響先算大的
for (int j = k; j >= 0; j--) {
dp[u][j] += dp[v][0] + w * 2; // 加上子樹放0個機器人的情況, 等於一個回程
// 在子樹放m( 1 <= m <= j)個機器人的情況
for (int m = 1; m <= j; m++)
dp[u][j] = min(dp[u][j], dp[u][j - m] + dp[v][m] + w * m); // 在子樹放m個機器人,相當於同一段路走m次
}
}
}
int main() {
int u, v, w;
cin >> n >> s >> k;
for (int i = 1; i < n; i++) {
cin >> u >> v >> w;
edge[u].push_back(Edge(v, w));
edge[v].push_back(Edge(u, w));
}
memset(dp, 0, sizeof(dp));
memset(vis, 0, sizeof(vis));
dfs(s);
cout << dp[s][k] << endl;
return 0;
}