次小生成树:在最小生成树(MST)的基础上求得。
算法步骤:
- 基础:求出最小生成树,并且标记最小生成树的所有边
- 将一个不是最小生成树的边加入到最小生成树中,此时有 N 条边( N 个点),必定存在一个“环”,现在需要去除“环”上一条边使之成为一课生成树。
- 在“环”中去除的边为除了加入边以外的权值最大的边(因为除了次小,就是最小)。
- 枚举不在最小生成树上的每一条边,将其进行2、3步操作,然后选择更改后权值最小的生成树,该生成树为次小生成树。
配题:HDU 4081
题意:无向图,有 N 个点,每个点的坐标和该点具有的人口数告诉你,秦始皇想将N个点连通起来,用最小的代价去铺路(总里程数最小),有一个巫师,可以将一条路的代价将为 0(巫师选中的这条路必须被建造)。求 一条路的一个特殊比值 的最大值。
思想:
- totalroad显然是最小生成树总权值。
- 如果巫师选中的路正好在最小生成树上,那么。
- 如果巫师选中的路不在最小生成树上,那么,此时变成了次小生成树的一个子问题,添加uv之后,去除环中的除(uv)以外的最大边权的边。
- 选出比值最大的作为输出即可。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#include<cmath>
#include<iomanip>
#define inf 0x7fffffff
using namespace std;
const int maxn = 1005;
struct POINT
{
double x, y;
int p;
}point[maxn];
struct NODE
{
int v;
double dis;
};
vector<NODE>graph[maxn];
int n;
double sum;
double dp[maxn][maxn];
bool used[maxn][maxn];
double cal_dis(POINT& p1, POINT& p2)
{
return sqrt((p1.x - p2.x)*(p1.x - p2.x) + (p1.y - p2.y)*(p1.y - p2.y));
}
double Max(double x, double y)
{
if (x > y) return x;
return y;
}
double Prim(int source)
{
double dis[maxn];
bool vis[maxn];
double mindis;
double sum = 0;
int father[maxn];
int pos;
memset(vis, false, sizeof(vis));
memset(dis, inf, sizeof(dis));
memset(dp, 0, sizeof(dp));
memset(used, false, sizeof(used));
for (int i = 0; i < graph[source].size(); i++)
{
int v = graph[source][i].v;
dis[v] = graph[source][i].dis;
father[v] = source;
}
vis[source] = true;
for (int k = 1; k < n; k++)
{
mindis = inf;
for (int j = 1; j <= n; j++)
{
if (!vis[j] && dis[j] < mindis)
{
mindis = dis[j];
pos = j;
}
}
sum += mindis;
vis[pos] = true;
used[father[pos]][pos] = used[pos][father[pos]] = true;
for (int j = 0; j < graph[pos].size(); j++)
{
int v = graph[pos][j].v;
double w = graph[pos][j].dis;
if (vis[v])
{
dp[pos][v] = dp[v][pos] = Max(dis[pos], dp[father[pos]][v]);
}
else
{
if (dis[v] > w)
{
dis[v] = w;
father[v] = pos;
}
}
}
}
return sum;
}
int main()
{
int T;
cin>> T;
while (T--)
{
cin>> n;
for (int i = 1; i <= n; i++)
{
cin>> point[i].x>> point[i].y>> point[i].p;
graph[i].clear();
}
for (int i = 1; i <= n; i++)
{
for (int j = i+1; j <= n; j++)
{
double dis = cal_dis(point[i], point[j]);
NODE node;
node.v = j;
node.dis = dis;
graph[i].push_back(node);
node.v = i;
graph[j].push_back(node);
}
}
double mst_sum = Prim(1);
double res = -1;
for (int i = 1; i <= n; i++)
{
for (int j = 0; j < graph[i].size(); j++)
{
int u = i;
int v = graph[i][j].v;
int w = graph[i][j].dis;
if (used[u][v])
{
res = Max(res, (point[u].p + point[v].p)*1.0/(mst_sum - w));
}
else
{
res = Max(res, (point[u].p + point[v].p)*1.0/(mst_sum - dp[u][v]));
}
}
}
cout<< fixed<< setprecision(2);
cout<< res<< endl;
}
return 0;
}