UVA1494Qin Shi Huang's National Road System(LA5713)(HDU4081)

次小生成树的应用

总思路是枚举每条边作为那条免费的路;

当你确定一条免费的路之后,相当于在这两个点之间加了一条长度为0的边之后求MST;

根据刘汝佳所说,MST就是把所有环中的最大边删除,我们首先要求出MST

如果枚举的边属于MST,则他原来不是环上的最大边,现在也不是,该这条边为0后MST的结构不变,只是MST的总长度少了这条边的长度;  

 如果枚举的边不属于MST,则他原来肯定是某个环上的最大边,这条边长度为0之后删除的“最大边”就是原来环上的第二大的边,也就是原MST结构中这两个点之间的最大边;


在求出MST后我们可以预处理求出所有(i, j)之间的最大边


所求的MST需要这些内容

//1.某边是否属于MST

//2.MST上没两个点间的最大边
//3.MST的邻接表,用于求所有(i, j)之间的最大边


#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;
const int maxn = 1000+10;
struct Node
{
    int from, to;
    double dist;
    Node(int from, int to, double dist)
    {
        this->from = from;
        this->to = to;
        this->dist = dist;
    }
    bool operator<(const struct Node &ans)const
    {
        return dist > ans.dist;
    }
};

double popu[maxn], edge[maxn][maxn], sum;
int x[maxn], y[maxn];

bool vis[maxn], flag[maxn][maxn];//是否属于树
double dist[maxn];
vector<int> adj[maxn];//MST的邻接表

double EdgeM[maxn][maxn];//每两个点间的最大边

int tcase, n;
void Init()
{
    scanf("%d", &n);
    for(int i = 1; i <= n; i++)
        scanf("%d%d%lf", &x[i], &y[i], &popu[i]);
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= n; j++)
        {
            double xx = x[i] - x[j];
            double yy = y[i] - y[j];
            edge[i][j] = sqrt(xx*xx+yy*yy);
        }
}

void Prim()
{
    priority_queue<struct Node> myQue;
    for(int i = 1; i <= n; i++)
    {
        dist[i] = -1;
        vis[i] = false;
        adj[i].clear();
        memset(flag[i], false, sizeof(flag[i]));
    }
    sum = 0;//MST的总长度
    dist[1] = 0;
    vis[1] = true;
    for(int i = 2; i <= n; i++)//以1为起始集合
    {
        dist[i] = edge[1][i];
        myQue.push(Node(1, i, dist[i]));
    }
    while(!myQue.empty())
    {
        struct Node ans = myQue.top();
        myQue.pop();
        int f = ans.from;
        int u = ans.to;
        if(vis[u])
            continue;
        sum += edge[f][u];
        vis[u] = true;//改点已经加入MST了
        flag[f][u] = flag[u][f] = true;
        adj[u].push_back(f);
        adj[f].push_back(u);
        for(int v = 1; v <= n; v++)
            if((v != u && !vis[v]) && (dist[v] == -1 || dist[v] > edge[u][v]))
            {
                dist[v] = edge[u][v];
                myQue.push(Node(u, v, dist[v]));
            }
    }
}

void DFS(int root, int cur, double MM)
{
    EdgeM[root][cur] = MM;
    vis[cur] = true;
    for(vector<int>::iterator it = adj[cur].begin(); it != adj[cur].end(); it++)
        if(!vis[*it])
            DFS(root, *it, max(MM, edge[cur][*it]));

}

void Solve()
{
    for(int i = 1; i <= n; i++)
    {
        memset(vis, false, sizeof(vis));
        DFS(i, i, 0);
    }

    double ans = -1;
    for(int i = 1; i <= n; i++)
        for(int j = 1; j < i; j++)//枚举super的那条路
        {
            if(flag[i][j])
                ans = max(ans, (popu[i]+popu[j])/(sum-edge[i][j]));
            else
                ans = max(ans, (popu[i]+popu[j])/(sum-EdgeM[i][j]));

        }
    printf("%.2lf\n", ans);
}

int main()
{
    scanf("%d", &tcase);
    while(tcase--)
    {
        Init();
        Prim();
        Solve();
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值