题面
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<algorithm>
#include<queue>
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(相当于只变了数据规模)