标签:最小 等于 格式 集合 思路 mst 连接 std name
? 最小生成树是一副连通加权无向图中一棵权值最小的生成树.
? 在一个有n个点的图中, 在所有边中选择n - 1条边, 连接所有n个点, 使这n - 1条边的权值和是所有方案中最小的.
? 最小生成树在算法竞赛中很少单独考察, 常与图论及其他树上算法结合, 因此对于最小生成树的掌握是十分必要的.
基本思路
? 将所有点视为单独集合, 并将所有边按照边权从小到大排序, 然后顺序枚举每一条边. 如果这条边连接两个不同集合, 就将此边加入最小生成树中, 并合并这两个不同集合, 直至取了n - 1边.
? 对于不同点的关系,使用并查集维护.
代码
#include<bits/stdc++.h>
using namespace std;
struct Edge
{
int dis, from, to;
} edge[1000001];
int ans;
int cnt;
int num_edge;
int n, m;//点数, 边数
int bin[1000001];
void add(int from, int to, int dis)
{
edge[++cnt].dis = dis;
edge[cnt].to = to;
edge[cnt].from = from;
}
int find(int x)
{
if(bin[x] == x) return x;
return bin[x] = find(bin[x]);
}//查询
void uni(int x, int y)
{
bin[find(x)] = find(y);
}//合并
bool check(int x, int y)
{
if(find(x) == find(y)) return true;
return false;
}//判断
bool cmp(Edge x, Edge y)
{
return x.dis < y.dis;
}
void Kruskal()
{
for(int i = 1;i <= n;i++)
bin[i] = i;
sort(edge + 1, edge + 1 + m, cmp);
//未建图情况下构建最小生成树
/*for(int i = 1;i <= m;i++)
{
if(! check(edge[i].from, edge[i].to))
{
add(edge[i].from, edge[i].to, edge[i].dis);
add(edge[i].to, edge[i].from, edge[i].dis);
uni(edge[i].from, edge[i].to);
}
}*/
for(int i = 1;i <= m;i++)
{
if(check(edge[i].to, edge[i].from))
continue;
ans += edge[i].dis;
uni(edge[i].from, edge[i].to);
if(++num_edge == n - 1)//树中边数 = 点数 - 1;
break;
}
}
int main()
{
scanf("%d %d", &n, &m);
for(int i = 1;i <= m;i++)
{
int x, y, z;
scanf("%d %d %d", &x, &y, &z);
add(x, y, z);
}
Kruskal();
printf("%d", ans);
return 0;
}
? Kruskal使用贪心思想, 每次选取的边可以连接两不同集合且代价最小, 因此最后一定可以将n个点合并为一个集合并使总代价最小.
? 时间复杂度:O(m*logm), m为边数.
? 另外还有Prim算法也常用于求最小生成树,时间复杂度为O(n^2), 与Kruskal在实际使用时差距不大, 也可另作学习,
AAA地区在地震过后,连接所有村庄的公路都造成了损坏而无法通车。政府派人修复这些公路。
给出A地区的村庄数n和公路数m,公路是双向的。并告诉你每条公路的连着哪两个村庄,并告诉你什么时候能修完这条公路。问最早什么时候任意两个村庄能够通车,即最早什么时候任意两条村庄都存在至少一条修复完成的道路(可以由多条公路连成一条道路)
第1行两个正整数n, m
下面m行,每行3个正整数x, y, t,告诉你这条公路连着x, y两个村庄,在时间t时能修复完成这条公路。
如果全部公路修复完毕仍然存在两个村庄无法通车,则输出?1,否则输出最早什么时候任意两个村庄能够通车。
? 接近裸题, 直接根据题意求最小生成树, 唯一不同是因为题中需判断是否可以全连通, 所以在算法执行过程中直接统计全部边数, 最后根据边数进行判断.
#include<bits/stdc++.h>
using namespace std;
int n, m;
int cnt;
int num_edge;
int bin[1000001];
int ans;
struct Edge
{
int from, to, dis;
} edge[1000001];
void add(int from, int to, int dis)
{
edge[++cnt].from = from;
edge[cnt].to = to;
edge[cnt].dis = dis;
}
int find(int x)
{
if(bin[x] == x) return x;
return bin[x] = find(bin[x]);
}
void uni(int x, int y)
{
bin[find(x)] = find(y);
}
bool check(int x, int y)
{
if(find(x) == find(y)) return true;
return false;
}
bool cmp(Edge a, Edge b)
{
return a.dis < b.dis;
}
void Kruskal()
{
sort(edge + 1, edge + 1 + m, cmp);
for(int i = 1;i <= n;i++)
bin[i] = i;
for(int i = 1;i <= m;i++)
{
if(check(edge[i].from, edge[i].to)) continue;
ans = max(ans, edge[i].dis);
uni(edge[i].from, edge[i].to);
//if(++num_edge == n - 1) break;
num_edge++;
}
}
int main()
{
scanf("%d %d", &n, &m);
for(int i = 1;i <= m;i++)
{
int u, v, w;
scanf("%d %d %d", &u, &v, &w);
add(u, v, w);
}
Kruskal();
if(num_edge >= n - 1)
{
printf("%d", ans);
return 0;
}
printf("-1");
return 0;
}
又到了一年一度的明明生日了,明明想要买B样东西,巧的是,这B样东西价格都是A元。
但是,商店老板说最近有促销活动,也就是:
如果你买了第I样东西,再买第J样,那么就可以只花K{I,J}元,更巧的是, K{I,J}竟然等于K{J,I}。
现在明明想知道,他最少要花多少钱。
第一行两个整数,A,B
接下来B行,每行B个数,第I行第J个为K{I,J}。
我们保证K{I,J}=K{J,I}, 并且K{I,I}=0。
特别的,如果K{I,J} = 0,那么表示这两样东西之间不会导致优惠。
一个整数,为最小要花的钱数。
? 直接最小生成树, 但因为两件物品先买哪一件优惠相同, 因此对于输入格式, 需判断i与j的大小关系以及w是否为0, 在进行Kruska前先将买第一件物品时由0到每一件物品建边, 代价为a.
#include<bits/stdc++.h>
using namespace std;
int a, b;
int cnt;
int ans;
struct Edge
{
int from, to;
int dis;
} edge[1000001];
int bin[1000001];
int num_edge;
void add(int from, int to, int dis)
{
edge[++cnt].from = from;
edge[cnt].to = to;
edge[cnt].dis = dis;
}
int find(int x)
{
if(x == bin[x]) return x;
return bin[x] = find(bin[x]);
}
void uni(int x, int y)
{
bin[find(x)] = find(y);
}
bool check(int x, int y)
{
if(find(x) == find(y)) return true;
return false;
}
bool cmp(Edge a, Edge b)
{
return a.dis < b.dis;
}
void Kruskal()
{
for(int i = 1;i <= b;i++)
bin[i] = i;
sort(edge + 1, edge + 1 + cnt, cmp);
for(int i = 1;i <= cnt;i++)
{
if(check(edge[i].from, edge[i].to)) continue;
ans += edge[i].dis;
uni(edge[i].from, edge[i].to);
if(++num_edge == b) break;
}
}
int main()
{
scanf("%d %d", &a, &b);
for(int i = 1;i <= b;i++)
{
for(int j = 1;j <= b;j++)
{
int w;
scanf("%d", &w);
if(i < j && w)
add(i, j, w);
}
}
for(int i = 1;i <= b;i++) add(0, i, a);
Kruskal();
printf("%d", ans);
return 0;
}
标签:最小 等于 格式 集合 思路 mst 连接 std name
原文地址:https://www.cnblogs.com/keliven/p/11296030.html