信息学奥赛一本通 1348:【例4-9】城市公交网建设问题

【题目链接】

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;
}

猜你喜欢

转载自blog.csdn.net/lq1990717/article/details/128755049