题意
有 n 座城市,m 条可能的道路。每个城市有人口数 P,每条路有长度 C。把 n 座城市连成一个生成树。特别的,可以免去任意一条路 X 的花费。求一个最佳方案,使 X两端城市的人口数之和 / 生成树中其他路的花费之和 的比值最大,输出这个比值。
思路
要使比值最大,自然分母越小越好,这就牵扯到了最小生成树的问题。
但这个问题并不是严格的最小生成树的问题,最优解中,免去花费的这条边不一定在最小生成树中。
算法过程
首先求原图的最小生成树,然后枚举图上的每一条边。
**如果边在最小生成树中,比值 = 两端人口和 / SMT - 这条边权
如果边不在最小生成树中,比值 = 两端人口和 / SMT - 最小生成树中两点之间路径里最大边权**
不断取最小值更新答案即可。
这里边不在最小生成树中的情况,用到的那个最大边权,恰恰就是次小生成树种的 maxe 数组,所以把这个题归为次小生成树相关问题。
题目链接
http://acm.hdu.edu.cn/showproblem.php?pid=4081
AC代码
#include<cstdio>
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
typedef pair<int, int> P;
const int maxn = 100 + 10;
const int inf = 0x3f3f3f3f;
int cas, n, m, s, t, c;
int G[maxn][maxn];
int ans;
bool f;
int dis[maxn];
P path[maxn];
int pre[maxn];
int it;
int prim(int s)
{
int res = 0, cnt = 0;
memset(dis, inf, sizeof dis);
priority_queue<P, vector<P>, greater<P> > qu;
dis[s] = 0;
qu.push(P(dis[s], s));
while(qu.size())
{
int u = qu.top().first, v = qu.top().second;
qu.pop();
if(u > dis[v]) continue;
if(ans == -1 && v != 1) path[it++] = P(v, pre[v]);
cnt ++;
res += u;
dis[v] = -1;
for(int i= 1; i<= n; i++)
if(dis[i] > G[v][i])
{
pre[i] = v;
dis[i] = G[v][i];
qu.push(P(dis[i], i));
}
}
if(cnt != n) return -1;
return res;
}
int main()
{
scanf("%d", &cas);
while(cas --)
{
memset(G, inf, sizeof G);
ans = -1, f = false, it = 0;
scanf("%d %d", &n, &m);
for(int i= 1; i<= m; i++)
{
scanf("%d %d %d", &s, &t, &c);
G[s][t] = G[t][s] = c;
}
ans = prim(1);
if(ans == -1) f = true;
else for(int i= 0; i< it; i++)
{
int u = path[i].first, v = path[i].second;
int temp = G[u][v];
G[u][v] = G[v][u] = inf;
if(ans == prim(1))
{
f = true;
break;
}
G[u][v] = G[v][u] = temp;
}
if(f) puts("Not Unique!");
else printf("%d\n", ans);
}
return 0;
}