1.学习图的最小生成树
简单说来大概可以将“最小生成树”视作“最短路”+“树式结构”,而“最短路”与“树”我们都已经有了些基础,学这个也就感觉没那么难了。
2.做题
昨天晚一点的时候终于把这题整出来了。
链式前向星的存储结构就类似于链表,就是上图右侧的样子。
其中to记录“指向的节点”,w表示“两点间的边的权值”,next则是记录数组下标作为指针来连接各节点,而head数组就是起到更新指针的作用
清楚链式前向星的存储原理后就能把这题做出来了。
#include <bits/stdc++.h>
using namespace std;
struct Edge {
int v,w,nxt;
};
Edge e[500010];
int head[100010],cnt=0;
inline void addEdge(int u,int v,int w) {
e[++cnt].v=v;
e[cnt].w=w;
e[cnt].nxt=head[u];
head[u]=cnt;
}
int n,m,s;
int dis[100010];
struct node { //堆节点
int u,d;
bool operator <(const node& rhs) const {
return d>rhs.d;
}
};
inline void Dijkstra() {
for ( int i=1; i<=n; i++) dis[i]=2147483647;
dis[s]=0;
priority_queue<node> Q; //堆
Q.push((node) {
s,0
});
while (!Q.empty()) {
node fr=Q.top();
Q.pop();
int u=fr.u,d=fr.d;
if (d!=dis[u]) continue;
for ( int i=head[u]; i; i=e[i].nxt) {
int v=e[i].v,w=e[i].w;
if (dis[u]+w<dis[v]) {
dis[v]=dis[u]+w;
Q.push((node) {
v,dis[v]
});
}
}
}
}
int main() {
cin>>n>>m>>s;
int X,Y,Z;
for ( int i=1; i<=m; i++) {
cin>>X>>Y>>Z;
addEdge(X,Y,Z);
}
Dijkstra();
for ( int i=1; i<=n; i++) printf("%d ",dis[i]);
return 0;
}
(2).【模板】最小生成树 - 洛谷
经典模板题,就不解释了,注意一下如果无法找到最小生成树,就要输出“orz”
#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
struct edge
{
int u,v,w;
}e[1000100];/*用一个结构体存储边的关系.数组大小需比边数大1*/
int n,m,parent[200100],sum,count1;/*并查集需要用到的变量。f【】数组大小需比点数大1*/
bool cmp(struct edge a,struct edge b)
{
if(a.w < b.w) return 1;
else return 0;
}
int find(int x)/*查找父节点*/
{
if(parent[x] != x)
parent[x] = find(parent[x]);
return parent[x];
}
int union_parent(int x,int y)/*连接两节点*/
{
if(find(x) != find(y))
{ parent[find(y)]=find(x); return 1;}
else return 0;
}
int main()
{
int i,j,k;
cin>>n>>m;/*n个顶点,m条边*/
for(i=1;i<=m;i++)
cin>>e[i].u>>e[i].v>>e[i].w;
sort(e+1,e+m+1,cmp);/*对结构体降序排序*/
// for(i=1;i<=m;i++) //输出检查排序
// cout<<e[i].u<<" "<<e[i].v<<" "<<e[i].w<<endl;
/*并查集初始化*/
for(i=0;i<=n;i++)
parent[i]=i;
for(i=1;i<=m;i++)
{
/*判断一条边的两个顶点是否连通,即判断是否在同一个集合中*/
if( union_parent(e[i].u,e[i].v) )/*非0即连通*/
{
count1++;
sum+=e[i].w;
}
if(count1 == n-1) break;/*选用n-1条边后退出循环*/
}
for(i=2;i<=n;i++)
if(find(parent[i-1]) != find(parent[i])) {cout<<"orz"<<endl;system("pause");return 0;}
cout<<sum<<endl;
system("pause");
return 0;
}
(3).拆地毯 - 洛谷
也是道模板题,对“美丽度”进行排序后求树就好了,只不过这里要求的是“最大生成树”
#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
struct edge
{
int u,v,w;
}e[100100];/*用一个结构体存储边的关系.数组大小需比边数大1*/
int n,m,k,parent[100100],sum,count1;/*并查集需要用到的变量。f【】数组大小需比点数大1*/
bool cmp(struct edge a,struct edge b)
{
if(a.w > b.w) return 1;
else return 0;
}
int find(int x)/*查找父节点*/
{
if(parent[x] != x)
parent[x] = find(parent[x]);
return parent[x];
}
int union_parent(int x,int y)/*连接两节点*/
{
if(find(x) != find(y))
{ parent[find(y)]=find(x); return 1;}
else return 0;
}
int main()
{
int i,j,v;
cin>>n>>m>>k;/*n个顶点,m条边*/
for(i=1;i<=m;i++)
cin>>e[i].u>>e[i].v>>e[i].w;
sort(e+1,e+m+1,cmp);/*对结构体升序排序*/
// for(i=1;i<=m;i++) //输出检查排序
// cout<<e[i].u<<" "<<e[i].v<<" "<<e[i].w<<endl;
/*并查集初始化*/
for(i=0;i<=n;i++)
parent[i]=i;
for(i=1;i<=m;i++)
{
/*判断一条边的两个顶点是否连通,即判断是否在同一个集合中*/
if( union_parent(e[i].u,e[i].v) )/*非0即连通*/
{
count1++;
sum+=e[i].w;
}
if(count1 == k) break;/*选用n-1条边后退出循环*/
}
cout<<sum<<endl;
system("pause");
return 0;
}