洛谷P2330 [SCOI2005]繁忙的都市

题面

n(1<=n<=300)个顶点,m(1<=m<=100000)条边,边有权值c(1<=c<=10000),越小代表选中越优。求应选中的边的最大值。

约束:
1.选中的所有边必须覆盖所有端点
2.在满足要求1的情况下,选中边尽量少。
3.在满足要求1、2的情况下,改造的那些道路中权值最大的道路分值尽量小。

分析

题目的约束表明这是一个最小生成树的题目,通用的做法有Prim和Kruskal两种。
前者步骤:选中一个最短边,然后加入与其相连的最短边(即加入一个顶点),以此类推。在每一步都加入一个未被选过且与某个已选中点相邻的点,达到n个点时即完成,复杂度O(n^2),n为顶点数,适合稠密图。

后者步骤:每一步都选择最短的边,不会构成环时即加入,加入n-1条边即可。复杂度O(eloge),e为边数,适合稀疏图。

代码

用了Prim代码(复习一下)

#include "cstdlib"
#include "iostream"
#include "cstdio"
#include "cstring"
#include
#include
using namespace std;
int route[305][305];
int vis[305];//是否已经加入
struct Edge
{
	int u,v, w;
	Edge() {}
	Edge(int u,int v,int w):u(u),v(v),w(w){}
	bool operator < (const Edge&other)const//小根堆,方便每次直接取出相连的最短边
	{
		if (w >= other.w) { return 1; }
		else return 0;
	}
};
priority_queue<Edge>q;//保存与已选中点相连的其他点
int main()
{
	ios::sync_with_stdio(false);
	int ans = 0;
	int n,m,u,v,c,minu,minv,minc=2e8;
	cin >> n>>m;
	for (int i = 1; i <= n; i++)
		for (int j = 1;j <= n;j++)route[i][j] = 2e8;
	for (int i = 0; i < m; i++)
	{
		cin >> u >> v >> c;
		route[u][v] = min(route[u][v], c);
		route[v][u] = min(route[v][u],c);
		if (c < minc)
		{
			minc = c;minu = u;minv = v;
		}
	}
	q.push(Edge(minu, minv, minc));
	Edge temp;
	for (int i = 0; i < n-1; i++)
	{
		Edge temp = q.top();
		q.pop();
		while (vis[temp.v]) { temp = q.top();q.pop();}
		minu = temp.u;minv = temp.v;ans = max(temp.w, ans);
		vis[minv] = 1;vis[minu] = 1;
		for (int j = 1;j <= n;j++) {
			if (!vis[j]) {
				if(route[minu][j]<=10000)q.push(Edge(minu, j, route[minu][j]));
				if (route[minv][j] <= 10000)q.push(Edge(minv, j, route[minv][j]));
			}
		}
	}
	cout << n-1<<" "<<ans;
	return 0;
}

重题P1547(相当于只变了数据规模)

你可能感兴趣的:(#,树)