信息学奥赛一本通 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中,即可得到有序序列。

关于set的比较规则:
set默认使用less<pair<int, int>>比较规则,仿函数类模板less的实现大体为:

template<class T>
struct Less
{
	bool operator () (T a, T b)
	{
		return a < b;
	}
}

此时T类型为pair<int, int>,那么代码中的a < b就是两个pair<int, int>对象之间的比较。
而pair类型已经重载了g各种比较运算符,比较规则是先比较第一个元素,如果第一个元素相等,再比较第二个元素。其大体实现为:

template<class T1, class T2>
struct pair
{
    T1 first;
    T2 second;
    bool operator < (pair<T1, T2> b)
    {
        if(first == b.first)
            return second < b.second;
        else
            return first < b.first;
    }
}

该比较规则就是我们预想的输出数对的比较规则:

  • 如果第一个数字不同,第一个数字小的数对排在前面。
  • 如果第一个数字相同,第二个数字小的数对排在前面

因此,在使用set时不用特意设其他的比较规则,使用默认的仿函数类less<pair<int, int>>即可。

解法2:Prim算法堆优化

求最小生成树使用Prim算法堆优化方法。
注意优先队列中要保存包含到达顶点及权值的Edge类型的对象。
保存边的处理方法与解法1相同。

解法3:Kruskal算法

Kruskal算法是加边法,把获得最小生成树过程中选择的边都加入到一个容器(set或vector)中进行排序,按规则输出该容器中的所有边即可。

【题解代码】

解法1:Prim算法 邻接矩阵 使用set排序
#include<bits/stdc++.h>
using namespace std;
#define N 105
struct Edge
{
	int v, w;
	Edge(){}
	Edge(int a, int b):v(a),w(b){}
};
int n, m, dis[N], from[N];//from[i]到顶点i的一条边是最小生成树中的边 
bool vis[N];
vector<Edge> edge[N];
set<pair<int, int>> 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)
			tree.insert(make_pair(min(from[u], u), max(from[u], u)));//from[u]和u的较小值为pair的first,较大值为second 
		for(Edge e : edge[u])
		{
			int v = e.v, w = e.w;
			if(vis[v] == false && dis[v] > w)
			{ 
				dis[v] = w;
				from[v] = u; 
			}
		}
	}
}
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();
	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 Edge
{
	int v, w;
	Edge(){}
	Edge(int a, int b):v(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];
vector<Edge> edge[N];
priority_queue<Edge> pq;
set<pair<int, int>> tree;//保存生成树的所有边 
void prim()//prim算法堆优化 
{
	memset(dis, 0x3f, sizeof(dis));
	dis[1] = 0;
	pq.push(Edge(1, 0));
	int visNum = 0;
	while(pq.empty() == false)
	{ 
		int u = pq.top().v;
		pq.pop();
		if(vis[u])
			continue;
		vis[u] = true;
		if(from[u] != 0)
			tree.insert(make_pair(min(from[u], u), max(from[u], u)));
		if(++visNum == n)
			break;
		for(Edge e : edge[u])
		{
			int v = e.v, w = e.w;
			if(vis[v] == false && dis[v] > w)
			{ 
				dis[v] = w;
				from[v] = u;
				pq.push(e);
			}
		}
	}
}
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();
	for(pair<int, int> p : tree)
        cout << p.first << "  " << p.second << 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){}
};
int n, m, fa[N], edgeNum;
vector<Edge> edges, tree;//edges:所有的边 tree:最小生成树的所有边 
void initFa(int n)
{
	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_edges(Edge a, Edge b)
{
	return a.w  < b.w;
}
bool cmp_tree(Edge a, Edge b)
{
	if(a.f == b.f)
		return a.t < b.t;
	else
		return a.f < b.f;
}
void kruskal()
{
	sort(edges.begin(), edges.end(), cmp_edges);//按照权值从小到大排序 
	for(Edge e :edges)
	{
		int f = e.f, t = e.t;
		if(find(f) != find(t))
		{
			tree.push_back(e);
			merge(f, t);
			if(++edgeNum == n-1)
				break;
		}
	}
}
int main()
{
	int f, t, w;
	cin >> n >> m;
	initFa(n);
	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 
	}
	kruskal();
	sort(tree.begin(), tree.end(), cmp_tree);//按照边的f从小到大,如f相同,按t从小到大排序。 
	for(Edge e : tree)
		cout << e.f << "  " << e.t << endl;
	return 0;
}

信息学奥赛一本通1255:迷宫问题是一个关于迷宫的问题。这个问题要求通过广搜算法来解决迷宫问题,找到走出迷宫的路径。具体来说,迷宫可以看成是由n×n的格点组成,每个格点只有两种状态, "." 和 "#" 。其中 "." 代表可通行的路径,"#" 代表不可通行的墙壁。通过广搜算法,我们可以搜索从起点到终点的路径,找到一条合法的路径即可。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [信息学奥赛一本通 1255:迷宫问题 | OpenJudge NOI 2.5 7084:迷宫问题](https://blog.csdn.net/lq1990717/article/details/124721407)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [c++信息学奥赛一本通1215题解](https://download.csdn.net/download/Asad_Yuen/87357807)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [信息学奥赛一本通(1255:迷宫问题)](https://blog.csdn.net/lvcheng0309/article/details/118879231)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值