【题目链接】
ybt 1348:【例4-9】城市公交网建设问题
输出时要求对于每条边,小的顶点写在前,大的顶点写在后。
多条边之间,第一个顶点小的排在前面。如果第一个顶点相同,则第二个顶点小的排在前面。
【题目考点】
1. 图论:最小生成树
- Prim算法
- Prim算法堆优化
- Kruskal算法
【解题思路】
该题难点是要保存最小生成树的所有边,对于不同的求最小生成树的算法,有不同的方法来完成。
解法1:Prim算法
Prim算法是加点法。每次在未选择的顶点中选择到已选择顶点边权值最小的顶点,将其加入最小生成树中。设选择的新顶点为u,这个新的顶点u与最小生成树中已选择的顶点from[u]
相连。from[u]
可以理解为:u顶点是从最小生成树中的哪个顶点连出来的。
因此(u, from[u])就是最小生成树中的一条边。
为了输出时有序,可以把表示最小生成树中边的所有数对都加到一个set中,设定比较规则,即可得到有序序列。
或先把所有数对都加到一个vector中,对该容器排序,而后得到有序序列。
解法2:Prim算法堆优化
求最小生成树使用Prim算法堆优化方法。
注意优先队列中要保存包含到达顶点及权值的Edge类型的对象。
保存边的处理方法与解法1相同。
解法3:Kruskal算法
Kruskal算法是加边法,把获得最小生成树过程中选择的边都加入到一个容器(set或vector)中进行排序,按规则输出该容器中的所有边即可。
【题解代码】
解法1:Prim算法 邻接矩阵 使用set排序
#include<bits/stdc++.h>
using namespace std;
#define N 105
struct Cmp//tree比较规则
{
bool operator () (pair<int, int> p1, pair<int, int> p2) const
{
if(p1.first == p2.first)
return p1.second < p2.second;
else
return p1.first < p2.first;
}
};
int n, m, dis[N], from[N];//from[i]到顶点i的一条边是最小生成树中的边
bool vis[N];
int edge[N][N];
set<pair<int, int>, Cmp> tree;//集合中每个元素都是最小生成树中的一条边,用于排序。保证每个元素第一个数小于第二个数
void prim()
{
memset(dis, 0x3f, sizeof(dis));
dis[1] = 0;
for(int k = 1; k <= n; ++k)
{
int u = 0;
for(int i = 1; i <= n; ++i)
if(vis[i] == false && (u == 0 || dis[i] < dis[u]))
u = i;
vis[u] = true;
if(from[u] != 0)//选择(u, from[u])这一条边
{
if(from[u] < u)
tree.insert(make_pair(from[u], u));//保证第一个数比第二个数小,这样输出时小的顶点在前,大的顶点在后
else
tree.insert(make_pair(u, from[u]));
}
for(int v = 1; v <= n; ++v)
{
if(edge[u][v])
{
int w = edge[u][v];
if(vis[v] == false && dis[v] > w)
{
dis[v] = w;
from[v] = u;//如果接下来顶点u要加入最小生成树,那么连进来的边为(v, u)
}
}
}
}
}
int main()
{
int f, t, w;
cin >> n >> m;
for(int i = 1; i <= m; ++i)
{
cin >> f >> t >> w;
edge[f][t] = edge[t][f] = w;
}
prim();
for(pair<int, int> p : tree)
cout << p.first << " " << p.second << endl;
return 0;
}
解法2:Prim算法堆优化 邻接表 使用vector及sort完成排序
#include<bits/stdc++.h>
using namespace std;
#define N 105
struct Pair
{
int x, y;
Pair(){
}
Pair(int a, int b)//调用该构造函数,自动使a,b中较小值赋值给x,较大值给y
{
if(a > b)
swap(a, b);
x = a;
y = b;
}
};
struct Edge
{
int t, w;
Edge(){
}
Edge(int a, int b):t(a),w(b){
}
bool operator < (const Edge &b) const
{
return b.w < w;
}
};
int n, m, dis[N], from[N];//from[i]到顶点i的一条边是最小生成树中的边
bool vis[N];
priority_queue<Edge> pq;
vector<Edge> edge[N];
vector<Pair> tree;//保存生成树的所有边
bool cmp(Pair a, Pair b)
{
if(a.x == b.x)
return a.y < b.y;
else
return a.x < b.x;
}
void prim()//prim算法堆优化
{
memset(dis, 0x3f, sizeof(dis));
dis[1] = 0;
pq.push(Edge(1, 0));
int visNum = 0;//顶点数
while(visNum < n && pq.empty() == false)
{
int u = pq.top().t;
pq.pop();
if(vis[u] == false)
{
vis[u] = true;
visNum++;
if(from[u] != 0)
tree.push_back(Pair(from[u], u));
for(int i = 0; i < edge[u].size(); ++i)
{
int v = edge[u][i].t, w = edge[u][i].w;
if(vis[v] == false && dis[v] > w)
{
dis[v] = w;
from[v] = u;
pq.push(edge[u][i]);
}
}
}
}
}
int main()
{
int f, t, w;
cin >> n >> m;
for(int i = 1; i <= m; ++i)
{
cin >> f >> t >> w;
edge[f].push_back(Edge(t, w));
edge[t].push_back(Edge(f, w));
}
prim();
sort(tree.begin(), tree.end(), cmp);
for(int i = 0; i < tree.size(); ++i)
cout << tree[i].x << " " << tree[i].y << endl;
return 0;
}
解法3:Kruskal算法 保存所有边 使用vector及sort完成排序
#include<bits/stdc++.h>
using namespace std;
#define N 105
struct Edge
{
int f, t, w;
Edge(){
}
Edge(int a, int b, int c):f(a),t(b),w(c){
}
bool operator < (Edge &b) const
{
return w < b.w;
}
};
int n, m, fa[N], edgeNum;
vector<Edge> edges;//所有的边
vector<Edge> tree;//最小生成树
void initFa()
{
for(int i = 1; i <= n; ++i)
fa[i] = i;
}
int find(int x)
{
if(x == fa[x])
return x;
else
return fa[x] = find(fa[x]);
}
void merge(int x, int y)
{
fa[find(x)] = find(y);
}
bool cmp(Edge a, Edge b)
{
if(a.f == b.f)
return a.t < b.t;
else
return a.f < b.f;
}
int main()
{
int f, t, w;
cin >> n >> m;
initFa();
for(int i = 1; i <= m; ++i)
{
cin >> f >> t >> w;
if(f > t)
swap(f, t);
edges.push_back(Edge(f, t, w));//加入的边保证f<t
}
sort(edges.begin(), edges.end());//按照权值从小到大排序
for(int i = 0; i < edges.size(); ++i)
{
f = edges[i].f, t = edges[i].t;
if(find(f) != find(t))
{
tree.push_back(edges[i]);
merge(f, t);
if(++edgeNum == n-1)
break;
}
}
sort(tree.begin(), tree.end(), cmp);//按照边的f从小到大,如f相同,按t从小到大排序。
for(int i = 0; i < tree.size(); ++i)
cout << tree[i].f << " " << tree[i].t << endl;
return 0;
}