一群小丑演员,以其出色的柔术表演,可以无限量的钻进同一辆汽车中,而闻名世界。
现在他们想要去公园玩耍,但是他们的经费非常紧缺。
他们将乘车前往公园,为了减少花费,他们决定选择一种合理的乘车方式,可以使得他们去往公园需要的所有汽车行驶的总公里数最少。
为此,他们愿意通过很多人挤在同一辆车的方式,来减少汽车行驶的总花销。
由此,他们可以很多人驾车到某一个兄弟的家里,然后所有人都钻进一辆车里,再继续前进。
公园的停车场能停放的车的数量有限,而且因为公园有入场费,所以一旦一辆车子进入到公园内,就必须停在那里,不能再去接其他人。
现在请你想出一种方法,可以使得他们全都到达公园的情况下,所有汽车行驶的总路程最少。
输入格式
第一行包含整数 n,表示人和人之间或人和公园之间的道路的总数量。
接下来 n 行,每行包含两个字符串 A、B 和一个整数 L,用以描述人 A 和人 B 之前存在道路,路长为 L,或者描述某人和公园之间存在道路,路长为 L。
道路都是双向的,并且人数不超过 20,表示人的名字的字符串长度不超过 10,公园用 Park
表示。
再接下来一行,包含整数 s,表示公园的最大停车数量。
你可以假设每个人的家都有一条通往公园的道路。
输出格式
输出 Total miles driven: xxx
,其中 xxx 表示所有汽车行驶的总路程。
输入样例:
10
Alphonzo Bernardo 32
Alphonzo Park 57
Alphonzo Eduardo 43
Bernardo Park 19
Bernardo Clemenzi 82
Clemenzi Park 65
Clemenzi Herb 90
Clemenzi Eduardo 109
Park Herb 24
Herb Eduardo 79
3
输出样例:
Total miles driven: 183
解析:
首先 ,去掉1号节点之后, 无向可能会分成若干个连通块。可以用深度优先遍历划分出图中的每个连通块,设连通块共有 T 个,若T>S,则本题无解。
对于每个连通块,在这个连通块内部求出它的最小生成树,然后从连通块中选出一个节点P与1号节点相连,其中无向边(1,p)的权值尽量小。
此时,我们已经得到了原无向图的一棵生成树,1号节点的度数为T。我们还可以尝试改动S-T条边,让答案更优。
考虑无向图中从节点1出发的每条边(1,x), 边权为z。 如果(1.x)还不在当前的生成树中,那么继续找到当前生成树中从X到1的路径上权值最大的边(u,v),边权为 w. 求出使得w-z最大的点Xo。 若Xo 对应的 Wo -Zo>0,则从树中删掉边(uo,vo). 加入边(1,xo)· 答案就会变小Wo-Zo。
重复上一步S-T次,或者直到Wo-Zo<=0,就得到了题目所求的最小生成树。
本题的难点在于如何换边,具体的操作过程可以参考这道题: 1148. 秘密的牛奶运输 (次小生成树)-CSDN博客
#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<utility>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<math.h>
#include<map>
#include<sstream>
#include<deque>
#include<unordered_map>
using namespace std;
typedef long long LL;
const int N = 25, M = 405;
int n,m, k;
struct Edge {
int a, b, c, f;
bool operator<(const Edge& t)const {
return c < t.c;
}
}edge[M];
int dist[N][N], fa[N];
int h[N], e[M], w[M], ne[M], idx;
map<string, int>mp;
int find(int a) {
if (fa[a] == a)return a;
return fa[a] = find(fa[a]);
}
void add(int a, int b, int c) {
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}
void dfs(int u, int f, int mx, int d[]) {
d[u] = mx;
for (int i = h[u]; i != -1; i = ne[i]) {
int j = e[i];
if (j == f)continue;
dfs(j, u, max(mx, w[i]), d);
}
}
int main() {
cin >> m ;
int tt = 0;
memset(h, -1, sizeof h);
for (int i = 1; i <= m; i++) {
string a, b;
int c;
cin >> a >> b >> c;
if (!mp.count(a))mp[a] = ++n;
if (!mp.count(b))mp[b] = ++n;
if (a == "Park")tt = mp[a];
if (b == "Park")tt = mp[b];
edge[i] = { mp[a],mp[b],c };
}
cin >> k;
sort(edge + 1, edge + 1 + m);
for (int i = 1; i <= n; i++) fa[i] = i;
int sum = 0;
for (int i = 1; i <= m; i++) {
int a = edge[i].a, b = edge[i].b, c = edge[i].c;
if (a == tt || b == tt)continue;
int pa = find(edge[i].a), pb = find(edge[i].b);
if (pa != pb) {
edge[i].f = 1;
sum += c;
fa[pa] = pb;
add(a, b, c), add(b, a, c);
}
}
for (int i = 0; i<=m && k > 0; i++) {
int a = edge[i].a, b = edge[i].b, c = edge[i].c;
if (a != tt && b != tt)continue;
int pa = find(edge[i].a), pb = find(edge[i].b);
if (pa != pb) {
k--;
edge[i].f = 1;
sum += c;
fa[pa] = pb;
add(a, b, c), add(b, a, c);
}
}
for (int i = 1; i <= n; i++)dfs(i, 0, 0, dist[i]);
vector<int>q;
for (int i = 1; i <= m ; i++) {
int a = edge[i].a, b = edge[i].b, c = edge[i].c;
if ((a != tt && b != tt) || edge[i].f)continue;
if (c < dist[a][b] ) {
q.push_back(dist[a][b] - c);
}
}
sort(q.begin(), q.end());
reverse(q.begin(), q.end());
for (int i = 0; i < q.size() && k>0; i++) {
sum -= q[i];
k--;
}
printf("Total miles driven: %d\n", sum);
return 0;
}