AcWing 野餐规划
Description
一群小丑演员,以其出色的柔术表演,可以无限量的钻进同一辆汽车中,而闻名世界。
现在他们想要去公园玩耍,但是他们的经费非常紧缺。
他们将乘车前往公园,为了减少花费,他们决定选择一种合理的乘车方式,可以使得他们去往公园需要的所有汽车行驶的总公里数最少。
为此,他们愿意通过很多人挤在同一辆车的方式,来减少汽车行驶的总花销。
由此,他们可以很多人驾车到某一个兄弟的家里,然后所有人都钻进一辆车里,再继续前进。
公园的停车场能停放的车的数量有限,而且因为公园有入场费,所以一旦一辆车子进入到公园内,就必须停在那里,不能再去接其他人。
现在请你想出一种方法,可以使得他们全都到达公园的情况下,所有汽车行驶的总路程最少。
Input
第一行包含整数n,表示人和人之间或人和公园之间的道路的总数量。
接下来n行,每行包含两个字符串A、B和一个整数L,用以描述人A和人B之前存在道路,路长为L,或者描述某人和公园之间存在道路,路长为L。
道路都是双向的,并且人数不超过20,表示人的名字的字符串长度不超过10,公园用“Park”表示。
再接下来一行,包含整数s,表示公园的最大停车数量。
你可以假设每个人的家都有一条通往公园的道路。
Output
- 输出“Total miles driven: xxx”,其中xxx表示所有汽车行驶的总路程。
Sample Input
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
Sample Output
Total miles driven: 183
题解:
最小生成树,最小k度生成树。
- 这题废话贼多,下面为翻译版本:给定一张N个点M条边的无向图,求出无向图的一棵最小生成树,满足1号节点的度数不超过给定的整数S。N <= 30。
- 如果度数无限制直接最小生成树就行了。那么现在对1号点有了限制。那我可以先不看1号点啊,即断掉与1号点相连的所有边。这样,一张图就被分成了若干个联通块(假设为T块。那么我们贪心的想,肯定是对这若干个联通块每块做一次最小生成树。这样肯定是最优的。那么做完T后,我们再从每个联通块中选一个点到1号点权值最小的连上。这样,我们就完美的得到了一个恰好T度的最小生成树。注意,恰好T度,说明什么,如果T < S的话,我们还可以将这棵T度生成树变成一个S/S - 1/S - 2/…/T + 1度生成树,说不定会更优呢!所以这时枚举与1相连的点x,如果x到1的边不在生成树中,那么就可以得到一个赚到的价值v = 生成树中1到x路径上最大的边权 - x到1的边权。每一轮取一个最大的赚到价值,进行S - T轮后退出。或者一旦最大赚到价值 < 0了,也退出。
我已经尽梨讲得明白了…写这题要有一定的码力
#include <iostream>
#include <cstdio>
#include <cstring>
#include <map>
#include <algorithm>
#define N 35
#define M 100005
#define inf 0x7fffffff
using namespace std;
struct E {int next, to, dis;} e[M], edge[M];
struct Ed {int u, v, w;} ed[M];
int n = 1, m, S, T, num, num_edge, ans, tot, g;
int h[N], fat[N], head[N], Max[N];
bool vis[N], tag[N], fromOne[N];
map<string, int> mp;
map<string, bool> b;
void add(int u, int v, int w)
{
e[++num].next = h[u];
e[num].to = v;
e[num].dis = w;
h[u] = num;
}
void add_edge(int u, int v, int w)
{
edge[++num_edge].next = head[u];
edge[num_edge].to = v;
edge[num_edge].dis = w;
head[u] = num_edge;
}
bool cmp(Ed x, Ed y) {return x.w < y.w;}
void dfs(int x)
{
vis[x] = 1, tag[x] = 1, tot++;
for(int i = h[x]; i != 0; i = e[i].next)
if(e[i].to != 1 && !vis[e[i].to]) dfs(e[i].to);
}
int getFat(int x)
{
if(x == fat[x]) return x;
return fat[x] = getFat(fat[x]);
}
void ddfs(int x, int fat)
{
for(int i = head[x]; i != 0; i = edge[i].next)
if(edge[i].to != fat)
{
Max[edge[i].to] = max(Max[x], edge[i].dis);
ddfs(edge[i].to, x);
}
}
void readIn()
{
b["Park"] = 1, mp["Park"] = 1;
for(int i = 1; i <= m; i++)
{
string t1, t2;
int u, v, w;
cin >> t1 >> t2 >> w;
if(!b[t1]) b[t1] = 1, mp[t1] = ++n;
if(!b[t2]) b[t2] = 1, mp[t2] = ++n;
u = mp[t1], v = mp[t2];
ed[i].u = u, ed[i].v = v, ed[i].w = w;
add(u, v, w), add(v, u, w);
}
cin >> S;
}
int main()
{
cin >> m;
readIn();
sort(ed + 1, ed + 1 + m, cmp);
for(int i = 1; i <= n; i++) fat[i] = i;
for(int i = 2; i <= n; i++)
if(!vis[i])
{
T++, tot = g = 0;
memset(tag, 0, sizeof(tag));
dfs(i);
for(int j = 1; j <= m; j++)
{
if(tag[ed[j].u] && tag[ed[j].v] && getFat(ed[j].u) != getFat(ed[j].v))
{
fat[getFat(ed[j].u)] = getFat(ed[j].v);
g++, ans += ed[j].w;
add_edge(ed[j].u, ed[j].v, ed[j].w), add_edge(ed[j].v, ed[j].u, ed[j].w);
}
if(g == tot - 1) break;
}
int Min = inf, pos;
for(int j = h[1]; j != 0; j = e[j].next)
if(tag[e[j].to] && e[j].dis < Min) Min = e[j].dis, pos = e[j].to;
ans += Min, fromOne[pos] = 1;
add_edge(1, pos, Min), add_edge(pos, 1, Min);
}
Max[1] = -inf, ddfs(1, 0);
for(int k = 1; k <= S - T; k++)
{
int flag = 0, cost = -inf, pos;
for(int i = h[1]; i != 0; i = e[i].next)
if(!fromOne[e[i].to] && cost < (Max[e[i].to] - e[i].dis) && Max[e[i].to] - e[i].dis > 0)
cost = Max[e[i].to] - e[i].dis, pos = e[i].to, flag = 1;
if(!flag) break;
ans -= cost, fromOne[pos] = 1;
}
cout << "Total miles driven: " << ans;
return 0;
}