点这里 |
---|
题意: 给定n个城市,每个城市都有一个坐标和人数。现要将所有的城市连接起来,花费为两个城市之间的距离。你可以将其中的一条路的花费,变为零,称其为魔法道路。假设A为魔法道路连接的两个城市的总人口,B为在这条魔法道路的基础上,连接所有城市的最小花费;要求输出最大的A / B
。
题解: 看上去好像是先构造出最小生成树,得到没有魔法道路下的最小花费,然后逐个去删边,去求每种情况下的A和B,最后拿到最大值。整理一下思路
- 构造最小生成树: 构造的是没有魔法道路下的最小生成树,先用Kruskal求得在没有魔法道路下,连接所有城市的最小花费。
- 删边: 生成一条魔法道路可以节省一条路的花费,转化过来,就相当于从最小生成树中删除了某条边。因此,我们应该从最小生成树上去枚举删除每一条边的情况,而不是全图枚举。
- 魔法道路 ?= 删除的边: 生成一条魔法道路,对我们的最小生成树的直观影响,就是可以少连一条边。但是,魔法道路必须是最小生成树上的边吗?可能我这么一说,大家就会觉得我前后矛盾了。
看这样一个例子:假设我们构造出来的最小生成树如下1->2->3->4
,再假设我在城市1和城市4之间,生成了一条魔法道路,那么此时我们的最小生成树就有可能变成1->2->3
以为1和4之间已经有一条魔法道路了,不需要我们另外修路。这种情况下,我们的确从最小生成树上删边了,但是删的,却不是我们的魔法道路。 - 魔法道路两端的总人口A: 再明确一条看似很矛盾的问题:我们要从最小生成树上删的边 ≠ 魔法道路 但是我们要求的总人口A确是魔法道路两端的城市,怎么办?既然我们一开始就选择了用Kruskal的算法来构造最小生成树,那么在这里,仍然可以用到并查集的思想。将与魔法道路一端的城市u连通的城市划分为一个集合T,与另一端城市v连通的城市划分到另一个集合S。那么我们想要的
总人口A = 集合T中的最大值 + 集合S中的最大值
。
过程中犯的错:
- 应该删最小生成树上的边 && 删边 ≠ 魔法道路 我一开始确实是枚举删最小生成树上的边,但是样例就没通过,原因再上面第三条就有解释,最理想的魔法道路可能本来就不在最小生成树上。然后我就去枚举删全图的边,全图,可想而知,结果只能是TLE,我还顽固地又加上了很多剪枝。直到我意识到删边 ≠ 魔法道路才能解决这个问题。
#include<bits/stdc++.h>
using namespace std;
const int N = 1e3 + 10;
int T, n, t;
int s[N];
double ans;
struct node{ int id, x, y, w;} g[N];
struct edge{ int u, v, w;double len;} e[500 * N];
vector<edge> V;
bool cmp(edge a, edge b){ return a.len < b.len;}
bool compare(node a, node b){ return a.w > b.w;}
int find(int x){ return s[x] == x ? x : s[x] = find(s[x]);}
double len(int i, int j){
double x = g[i].x - g[j].x, y = g[i].y - g[j].y;
return sqrt(x * x + y * y);
}
void kruskal(){
for(int i = 1; i <= n; i++) s[i] = i; V.clear();
sort(e, e + t, cmp);
for(int i = 0; i < t; i++){
int u = find(e[i].u), v = find(e[i].v);
if(u == v) continue;
s[v] = u;
ans += e[i].len;
V.push_back(e[i]);
}
}
void solve(){
double maxn = 0;
sort(g + 1, g + n + 1, compare);
for(int i = 0; i < V.size(); i++){
for(int j = 1; j <= n; j++) s[j] = j;
for(int j = 0; j < V.size(); j++){
if(i == j) continue;
int u = find(V[j].u), v = find(V[j].v);
if(u != v) s[v] = u;
}
for(int j = 2; j <= j; j++)
if(find(g[j].id) != find(g[1].id)){
double tem = (double)(g[j].w + g[1].w) / (ans - V[i].len);
maxn = max(maxn, tem);
break;
}
}
printf("%.2lf\n", maxn);
}
int main(){
scanf("%d", &T);
while(T--){
t = ans = 0;
scanf("%d", &n);
for(int i = 1; i <= n; i++){ scanf("%d%d%d", &g[i].x, &g[i].y, &g[i].w); g[i].id = i;}
for(int i = 1; i <= n; i++)
for(int j = i + 1; j <= n; j++)
e[t].u = i, e[t].v = j, e[t].len = len(i, j), e[t++].w = g[i].w + g[j].w;
kruskal();
solve();
}
return 0;
}