【Kruskal】公路建设(Road.exe, 1s, 64M)

 

公路建设(Road.exe, 1s, 64M)

【问题描述】

A国是一个新兴的国家,有N个城市,分别编号为1,2.3…N。政府想大搞公路建设,提供了优惠政策:对于每一个投资方案的预计总费用,政府负担50%,并且允许投资的公司对过往的汽车收取连续5年的养路费。世界各地的大公司纷纷投资,并提出了自己的建设方案,他们的投资方案包括这些内容:公路连接的两座城市的编号,预计的总费用(假设他们的预计总师准确的)。你作为A国公路规划局的总工程师,有权利决定每一个方案是否接受。但是政府给你的要求是:

1)要保证各个城市之间都有公路直接或间接相连。

2)因为是新兴国家,政府的经济实力还不强。政府希望负担最少的费用。

    因为大公司并不是同时提出方案,政府希望每接到一个方案,就可以知道当前需要负担的最小费用和接受的投资方案,以便随时开工。关于你给投资公司的回复可以等到开工以后再给。

注意:A国一开始是没有公路的。

【数据说明】A国的城市数目N<=500,投资的方案总数M<=2000。

【输入】输入文件名:Road.in

第1行有两个数字:N、M

第2行到第M+1行给出了各个投资方案,第i行的方案编号为i-1

编号小的方案先接到,一个方案占一行,每行有3个数字,分别是连接的两个城市编号a、b,和投资的预计总费用cost。

【输出】输出文件名:Road.out

输出文件共有M行。

每一行的第一个数字是当前政府需要负担的最少费用(保留1位小数),后面是X个数字,表示当前政府接受的方案的编号,不要求从小到大排列。但如果此时接受的所有投资方案不能保证政府的第一条要求,那么这一行只有一个数字0

【样例】

Road.in

Road.out

3 5

1 2 4

1 3 4

2 3 4

1 3 2

1 2 2

0

4.0 1 2

4.0 1 2

3.0 1 4

2.0 4 5

================================

kruskal

===========================

type
  node=record
         x,y,v,o:longint;
       end;
var
  n,m:longint;
  fa:array[1..250000]of node;
  f_fa:array[1..250000]of longint;
  fa_s:longint;
  map:array[1..500,1..500]of longint;
  odd:array[1..500,1..500]of longint;
  ans_odd:array[1..250000]of longint;
  f_bo:array[1..500]of boolean;
procedure init;
begin
  assign(input,'road.in');
  assign(output,'road.out');
  reset(input); rewrite(output);
end;

procedure terminate;
begin
  close(input); close(output);
  halt;
end;

function find(x:longint):longint;
begin
  if f_fa[x]=x then exit(x)
               else begin f_fa[x]:=find(f_fa[x]); exit(f_fa[x]); end;
end;

procedure merge(a,b:longint);
var
  x,y:longint;
begin
  x:=find(a);
  y:=find(b);
  f_fa[x]:=y;
end;

procedure qsort(s,t:longint);
var
  i,j:longint;
  x:longint;
  tem:node;
begin
  x:=fa[(s+t)shr 1].v;
  i:=s; j:=t;
  repeat
    while x<fa[j].v do dec(j);
    while fa[i].v<x do inc(i);
    if i<=j then
      begin
        tem:=fa[i];
        fa[i]:=fa[j];
        fa[j]:=tem;
        inc(i); dec(j);
      end;
  until i>j;
  if i<t then qsort(i,t);
  if s<j then qsort(s,j);
end;

procedure main;
var
  i,j,k:longint;
  x,y,z:longint;
  t:longint;
  fa_t,ans:longint;
begin
  readln(n,m);
  fillchar(f_bo,sizeof(f_bo),true);
  fillchar(map,sizeof(map),$7);
  t:=0;
  for i:=1 to m do
    begin
      readln(x,y,z);
      if map[x,y]>z then
        begin
          map[x,y]:=z;
          odd[x,y]:=i;
          odd[y,x]:=i;
          if f_bo[x] then
            begin
              f_bo[x]:=false;
              inc(t);
            end;
          map[y,x]:=z;
          if f_bo[y] then
            begin
              f_bo[y]:=false;
              inc(t);
            end;
        end;
      if t<n then writeln(0)
       else
        if t=n then
          begin
            fa_s:=0;
            for j:=1 to n do
              for k:=j+1 to n do
                if map[j,k]<100000 then
                begin
                  inc(fa_s);
                  fa[fa_s].x:=j;
                  fa[fa_s].y:=k;
                  fa[fa_s].v:=map[j,k];
                  fa[fa_s].o:=odd[j,k];
                end;
            qsort(1,fa_s);
            for j:=1 to n do f_fa[j]:=j;
            
            fa_t:=0; ans:=0;
            for j:=1 to fa_s do
              begin
                if fa_t=n-1 then break;
                if find(fa[j].x)<>find(fa[j].y) then
                  begin
                    inc(fa_t);
                    ans_odd[fa_t]:=fa[j].o;
                    ans:=ans+fa[j].v;
                    merge(fa[j].x,fa[j].y);
                  end;
              end;
            write(ans/2:0:1,' ');
            for j:=1 to fa_t do write(ans_odd[j],' ');
            writeln;
          end;
    end;
end;

begin
  init;
  main;
  terminate;
end.


Kruskal算法和Prim算法都是用于求解最小生成树的经典算法。下面我将分别介绍这两个算法的基本原理和C++代码实现。 1. Kruskal算法: Kruskal算法基于贪心思想,通过不断选择权重最小的边来构建最小生成树。具体步骤如下: - 将图中的所有边按照权重从小到大进行排序。 - 初始化一个空的森林,每个顶点都是一个独立的树。 - 依次选择权重最小的边,如果这条边连接的两个顶点不在同一棵树中,则将这条边加入最小生成树中,并将这两个顶点所在的树合并为一棵树。 - 重复上述步骤,直到最小生成树中包含了图中的所有顶点。 以下是使用C++实现Kruskal算法的代码示例: ```cpp #include <iostream> #include <vector> #include <algorithm> using namespace std; // 定义边的结构体 struct Edge { int src, dest, weight; }; // 定义并查集的数据结构 struct DisjointSet { int *parent, *rank; int n; DisjointSet(int n) { this->n = n; parent = new int[n]; rank = new int[n]; for (int i = 0; i < n; i++) { parent[i] = i; rank[i] = 0; } } int find(int u) { if (u != parent[u]) { parent[u] = find(parent[u]); } return parent[u]; } void merge(int x, int y) { int xroot = find(x); int yroot = find(y); if (rank[xroot] < rank[yroot]) { parent[xroot] = yroot; } else if (rank[xroot] > rank[yroot]) { parent[yroot] = xroot; } else { parent[yroot] = xroot; rank[xroot]++; } } }; // 定义比较函数,用于边的排序 bool compareEdges(Edge a, Edge b) { return a.weight < b.weight; } // Kruskal算法求解最小生成树 void kruskalMST(vector<Edge> edges, int V) { // 对边按照权重进行排序 sort(edges.begin(), edges.end(), compareEdges); // 创建并查集 DisjointSet ds(V); // 存储最小生成树的边 vector<Edge> mst; for (auto edge : edges) { int srcRoot = ds.find(edge.src); int destRoot = ds.find(edge.dest); // 如果这条边的两个顶点不在同一棵树中,则将这条边加入最小生成树中 if (srcRoot != destRoot) { mst.push_back(edge); ds.merge(srcRoot, destRoot); } } // 输出最小生成树的边 for (auto edge : mst) { cout << edge.src << " - " << edge.dest << " : " << edge.weight << endl; } } int main() { int V, E; cout << "Enter the number of vertices: "; cin >> V; cout << "Enter the number of edges: "; cin >> E; vector<Edge> edges(E); cout << "Enter the source, destination and weight of each edge:" << endl; for (int i = 0; i < E; i++) { cin >> edges[i].src >> edges[i].dest >> edges[i].weight; } kruskalMST(edges, V); return 0; } ``` 2. Prim算法: Prim算法是一种以顶点为中心的算法,通过不断选择与当前最小生成树相连的权重最小的边来构建最小生成树。具体步骤如下: - 初始化一个空的最小生成树,选择一个起始顶点。 - 将起始顶点加入最小生成树中,并将与起始顶点相连的边加入优先级队列。 - 从优先级队列中选择权重最小的边,如果这条边连接的顶点不在最小生成树中,则将这条边加入最小生成树中,并将与这个顶点相连的边加入优先级队列。 - 重复上述步骤,直到最小生成树中包含了图中的所有顶点。 以下是使用C++实现Prim算法的代码示例: ```cpp #include <iostream> #include <vector> #include <queue> using namespace std; // 定义边的结构体 struct Edge { int dest, weight; }; // 定义比较函数,用于边的排序 struct Compare { bool operator()(Edge a, Edge b) { return a.weight > b.weight; } }; // Prim算法求解最小生成树 void primMST(vector<vector<Edge>> graph, int V) { // 存储最小生成树的边 vector<Edge> mst; // 创建一个优先级队列,用于动态排序边 priority_queue<Edge, vector<Edge>, Compare> pq; // 创建一个数组,用于标记顶点是否已经加入最小生成树 vector<bool> visited(V, false); // 选择一个起始顶点 int src = 0; visited[src] = true; // 将起始顶点的所有边加入优先级队列 for (auto edge : graph[src]) { pq.push(edge); } while (!pq.empty()) { // 选择权重最小的边 Edge minEdge = pq.top(); pq.pop(); int dest = minEdge.dest; int weight = minEdge.weight; // 如果这条边连接的顶点不在最小生成树中,则将这条边加入最小生成树中 if (!visited[dest]) { mst.push_back(minEdge); visited[dest] = true; // 将与这个顶点相连的边加入优先级队列 for (auto edge : graph[dest]) { pq.push(edge); } } } // 输出最小生成树的边 for (auto edge : mst) { cout << edge.dest << " - " << edge.weight << endl; } } int main() { int V, E; cout << "Enter the number of vertices: "; cin >> V; cout << "Enter the number of edges: "; cin >> E; vector<vector<Edge>> graph(V); cout << "Enter the source, destination and weight of each edge:" << endl; for (int i = 0; i < E; i++) { int src, dest, weight; cin >> src >> dest >> weight; graph[src].push_back({dest, weight}); graph[dest].push_back({src, weight}); } primMST(graph, V); return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值