野餐规划

题目:野餐规划

网址:https://www.acwing.com/problem/content/349/

一群小丑演员,以其出色的柔术表演,可以无限量的钻进同一辆汽车中,而闻名世界。

现在他们想要去公园玩耍,但是他们的经费非常紧缺。

他们将乘车前往公园,为了减少花费,他们决定选择一种合理的乘车方式,可以使得他们去往公园需要的所有汽车行驶的总公里数最少。

为此,他们愿意通过很多人挤在同一辆车的方式,来减少汽车行驶的总花销。

由此,他们可以很多人驾车到某一个兄弟的家里,然后所有人都钻进一辆车里,再继续前进。

公园的停车场能停放的车的数量有限,而且因为公园有入场费,所以一旦一辆车子进入到公园内,就必须停在那里,不能再去接其他人。

现在请你想出一种方法,可以使得他们全都到达公园的情况下,所有汽车行驶的总路程最少。

输入格式

第一行包含整数(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

此题其实是在说:给定一张图,让生成一颗最小生成树,要求结点(“Park”)连的边最多不超过(s)条。
令结点(“Park”)(1)

  • 对于结点(1),若断开所有的连边,产生(T)个联通分量,那么结点(1)最少也得连(T)条边;
  • 最小生成树的子树也是最小生成树;

综上,我们可以进行以下过程:

  1. 划分联通块;
  2. 对每个联通块进行最小生成树运算;
  3. 考虑结点(1)对每个联通块的边中权值最小的那一条边,连接并累和;
    4. 进行(S - T)次迭代或直至不能继续更新,预处理每个节点到根节点的路径上的权值最大的边,对于每一条与结点(1)相连并且没被连接的边((1, x)),若结点(x)的最大权值边的权值大于该边,则替换。

实现细节挺多的。

代码如下:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<map>
#include<queue>
#define pii pair <int, int>
using namespace std;
const int SIZE = 900 + 5;
struct node
{
	int u, v, w;
	inline bool operator <(const node& lhs)
	{
		return w < lhs.w;
	}
};
vector <node> e, path;
vector <int> G[SIZE], W[SIZE];
map <string, int> vertice;// 映射结点编号
map <pii, bool> chosen;//标记边是否被纳入 当前生成树 当中
bool vis[SIZE];
int n, S, T = 0, tot = 0, ans = 0, dfn[SIZE], fa[SIZE], dp[SIZE];
pii f[SIZE];
void init()
{
	e.clear(), vertice.clear();
	for(int i = 1; i <= 25; ++ i) fa[i] = i;
	chosen.clear(), vertice["Park"] = ++ tot;
	memset(vis, false, sizeof(vis));
	memset(dfn, -1, sizeof(dfn));
	return;
}
//并查集
int get(int x)
{
	if(fa[x] == x) return x;
	return fa[x] = get(fa[x]);
}
//染色 
void dfs(int u)
{
	dfn[u] = T;
	int v, w;
	for(int i = 0; i < G[u].size(); ++ i)
	{
		v = G[u][i], w = W[u][i];
		if(v == 1) continue;
		e.push_back((node)
		{
			u, v, w
		});
		if(dfn[v] == -1) dfs(v);
	}
	return;
}
//最小生成树系统
void Kruskal()
{
	sort(e.begin(), e.end());
	int x, y, v1, v2;
	for(int i = 0; i < e.size(); ++ i)
	{
		x = e[i].u, y = e[i].v, v1 = get(x), v2 = get(y);
		if(v1 == v2) continue;
		fa[v2] = v1;
		chosen[make_pair(x, y)] = chosen[make_pair(y, x)] = 1;
		ans += e[i].w;
	}
	return;
}
//预处理 简单路径 上权值最大的边
void bfs()
{
	memset(dp, 0, sizeof(dp));
	memset(vis, false, sizeof(vis));
	queue <int> Q;
	while(!Q.empty()) Q.pop();
	int u, v, w;
	Q.push(1);
	vis[1] = true;
	dp[1] = -1;
	while(!Q.empty())
	{
		u = Q.front();
		Q.pop();
		for(int i = 0; i < G[u].size(); ++ i)
		{
			v = G[u][i], w = W[u][i];
			if(vis[v] || (!chosen[make_pair(u, v)] && !chosen[make_pair(v, u)])) continue;
			vis[v] = true;
			dp[v] = w;
			f[v] = make_pair(u, v);
			if(dp[v] < dp[u]) 
			{
				dp[v] = dp[u];
				f[v] = f[u];
			}
			Q.push(v);
		}
	}
	return;
}
int main()
{
	scanf("%d", &n);
	init();
	string A, B;
	int L;
	// input :
	for(int i = 0; i < n; ++ i)
	{
		cin >> A >> B >> L;
		if(vertice.find(A) == vertice.end()) vertice[A] = ++ tot;//这种形式很好
		if(vertice.find(B) == vertice.end()) vertice[B] = ++ tot;
		G[vertice[A]].push_back(vertice[B]), G[vertice[B]].push_back(vertice[A]);
		W[vertice[A]].push_back(L), W[vertice[B]].push_back(L);
	}
	//划分联通块 & 求出联通块内的最小生成树 
	for(int i = 2; i <= tot; ++ i)
	{
		e.clear();
		if(dfn[i] == -1)
		{
			++ T;
			dfs(i), Kruskal();
		}
	}
	scanf("%d", &S);
	if(S < T)
	{
		printf("-1");
		return 0;
	}
	//对根节点的边进行挑选 
	for(int i = 1; i <= T; ++ i)
	{
		int val = 1 << 30, v;
		for(int j = 0; j < G[1].size(); ++ j)
		{
			if(dfn[G[1][j]] != i) continue;
			if(val > W[1][j]) val = W[1][j], v = G[1][j];
		}
		ans += val;
		chosen[make_pair(v, 1)] = chosen[make_pair(1, v)] = 1;
	}
	//进行 S - T 次循环 
	int u, w, x, tmp = 0, cnt = S - T;
	pii path;
	while(cnt)
	{
		bfs();
		for(int i = 0; i < G[1].size(); ++ i)
		{
			u = G[1][i], w = W[1][i];
			if(chosen[make_pair(1, u)]) continue;
			w = W[1][i];
			if(tmp < dp[u] - w) 
			{
				tmp = dp[u] - w;
				path = f[u];
				x = u;
			}
		}
		-- cnt;
		if(tmp <= 0) break;
		ans -= tmp;
		chosen[make_pair(path.first, path.second)] = chosen[make_pair(path.second, path.first)] = 0;
		chosen[make_pair(x, 1)] = chosen[make_pair(1, x)] = 1;
		tmp = 0;
	}
	printf("Total miles driven: %d
", ans);
	return 0;
}
原文地址:https://www.cnblogs.com/zach20040914/p/13499162.html