prim算法
复杂度:
o
(
n
2
)
o(n^2)
o(n2),n为节点数
prim算法是一种基于贪心的算法,主要流程是:首先,将任意一点(习惯是1)加入树中,之后,选择和1相连的所有边,选择最短的路径,把该路径和点加入树中,接着继续扫描新加入的点,直到边数达到n-1,形成树为止。
洛谷P3366 模板:最小生成树
先贴下板子:
ll prim(){//防爆
int res=0;
rep(i,2,n){
dis[i]=INT_MAX;//把所有距离都设置为inf,方便比较
}
for(int i=head[1];~i;i=e[i].next){
dis[e[i].to]=min(dis[e[i].to],e[i].w);//把1加入后,寻找和1相连的所有边中最短的。
}
int now=1;
int counter=0;
while(counter<n-1){//需要找到n-1条边
int mi=INT_MAX;
vis[now]=1;
rep(i,1,n){
if(!vis[i]&&mi>dis[i]){
mi=dis[i];
now=i;//寻找目标边和节点
}
}
res+=mi;//更新答案
counter++;
for(int i=head[now];~i;i=e[i].next){
int v=e[i].to;
if(vis[v]) continue;
dis[v]=min(dis[v],e[i].w);
}
}
return res;
}
以上是用暴力的方式完成的prim算法,后面还可以利用堆进行优化。
AC代码
#include <iostream>
#include <map>
#include <vector>
#include <climits>
#include <algorithm>
#include <random>
#include <cstring>
#include <cstdio>
#include <map>
#include <set>
#include <bitset>
#define IOS ios_base::sync_with_stdio(0); cin.tie(0);
#define rep(i, a, n) for(register int i = a; i <= n; ++ i)
#define per(i, a, n) for(register int i = n; i >= a; -- i)
//#define ONLINE_JUDGE
using namespace std;
typedef long long ll;
const int mod=1e9+7;
template<typename T>void write(T x)
{
if(x<0)
{
putchar('-');
x=-x;
}
if(x>9)
{
write(x/10);
}
putchar(x%10+'0');
}
template<typename T> void read(T &x)
{
x = 0;char ch = getchar();ll f = 1;
while(!isdigit(ch)){if(ch == '-')f*=-1;ch=getchar();}
while(isdigit(ch)){x = x*10+ch-48;ch=getchar();}x*=f;
}
ll gcd(ll a,ll b){return b==0?a:gcd(b,a%b);}
ll lcm(ll a,ll b){return a/gcd(a,b)*b;};
ll ksm(ll a,ll n){//看是否要mod
ll ans=1;
while(n){
if(n&1) ans=(ans*a)%mod;
a=a*a%mod;
n>>=1;
}
return ans%mod;
}
const int maxn=2e5+10;
struct Edge{
int next,w,to;
}e[maxn<<1];
int cnt,head[maxn];
void add(int x,int y,int w){
e[cnt].to=y;
e[cnt].next=head[x];
e[cnt].w=w;
head[x]=cnt++;
}
int n,m;
int dis[maxn];
int vis[maxn];
ll prim(){
int res=0;
rep(i,2,n){
dis[i]=INT_MAX;
}
for(int i=head[1];~i;i=e[i].next){
dis[e[i].to]=min(dis[e[i].to],e[i].w);
}
int now=1;
int counter=0;
while(counter<n-1){
int mi=INT_MAX;
vis[now]=1;
rep(i,1,n){
if(!vis[i]&&mi>dis[i]){
mi=dis[i];
now=i;
}
}
res+=mi;
counter++;
for(int i=head[now];~i;i=e[i].next){
int v=e[i].to;
if(vis[v]) continue;
dis[v]=min(dis[v],e[i].w);
}
}
return res;
}
int vis2[maxn];
void dfs(int u){
vis2[u]=1;
for(int i=head[u];~i;i=e[i].next){
int v=e[i].to;
if(vis2[v]) continue;
dfs(v);
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
//===========================================================
read(n),read(m);
memset(head,-1,sizeof(head));
rep(i,1,m){
int x,y,w;
read(x),read(y),read(w);
add(x,y,w),add(y,x,w);
}
int ans=prim();
dfs(1);
rep(i,1,n){
if(vis2[i]==0){
cout<<i<<endl;
cout<<"orz"<<endl;
return 0;
}
}
write(ans);
//===========================================================
return 0;
}
堆优化
复杂度:
(
n
l
o
g
n
)
(nlogn)
(nlogn),n为节点数
之后可以利用堆优化,其实就是利用堆选择目前的最优选项。
priority_queue<Edge> que;
int vis[maxn];
int num;
ll prim(){
ll res=0;
vis[1]=1;
num++;
for(register int i=head[1];~i;i=e[i].next){
int v=e[i].to;
if(vis[v]) continue;
que.push(e[i]);
}
while(!que.empty()){
Edge a=que.top();
que.pop();
int u=a.to;
if(vis[u]) continue;
vis[u]=1;
num++;
res+=a.w;
for(register int i=head[u];~i;i=e[i].next){
int v=e[i].to;
if(vis[v]) continue;
que.push(e[i]);
}
}
return res;
}
题目是:https://ac.nowcoder.com/acm/problem/20568
这道题主要的坑点在于有的情况下要建立双向边,其他的就是裸的最小生成树问题了。
kruskal算法
复杂度:
e
l
o
g
e
eloge
eloge,e为边的数目
kruskal算法相对prim算法更好写,更暴力。链式前向星?不需要,什么图都不用存,直接存边的起始点、终点、权值就行了。之后按边权sort一下,然后逐一往树中加入这些边。同时,利用并查集判断加入边后是否会形成环(边的两个顶点是否在加入该边前已经链接在一起了),直到加入n-1条边为止。
模板:
const int maxn=2e5+10;
int s[maxn];
int n,m;
struct Edge{
int u,v,w;
}e[maxn];
bool cmp(const Edge&a,const Edge&b){
return a.w<b.w;
}
void init(){
rep(i,1,n) s[i]=i;
}
inline int find(int x){
if(x==s[x]) return x;
int r=x;
while(s[r]!=r) r=s[r];
while(x!=r){
int t=s[x];
s[x]=r;
x=t;
}
return r;
}
inline ll kruskal(){
ll res=0;
sort(e,e+m,cmp);
int cnt=0;
rep(i,0,m-1){
int u=find(e[i].u),v=find(e[i].v);
if(u==v) continue;
s[u]=v;
res+=e[i].w;
cnt++;
if(cnt==n-1) return res;
}
}
洛谷模板题:
#include <iostream>
#include <map>
#include <vector>
#include <climits>
#include <algorithm>
#include <random>
#include <cstring>
#include <cstdio>
#include <map>
#include <set>
#include <bitset>
#include <queue>
#define IOS ios_base::sync_with_stdio(0); cin.tie(0);
#define rep(i, a, n) for(register int i = a; i <= n; ++ i)
#define per(i, a, n) for(register int i = n; i >= a; -- i)
#define ONLINE_JUDGE
using namespace std;
typedef long long ll;
const int mod=1e9+7;
template<typename T>void write(T x)
{
if(x<0)
{
putchar('-');
x=-x;
}
if(x>9)
{
write(x/10);
}
putchar(x%10+'0');
}
template<typename T> void read(T &x)
{
x = 0;char ch = getchar();ll f = 1;
while(!isdigit(ch)){if(ch == '-')f*=-1;ch=getchar();}
while(isdigit(ch)){x = x*10+ch-48;ch=getchar();}x*=f;
}
ll gcd(ll a,ll b){return b==0?a:gcd(b,a%b);}
ll lcm(ll a,ll b){return a/gcd(a,b)*b;};
ll ksm(ll a,ll n){//看是否要mod
ll ans=1;
while(n){
if(n&1) ans=(ans*a)%mod;
a=a*a%mod;
n>>=1;
}
return ans%mod;
}
const int maxn=2e5+10;
int s[maxn];
int n,m;
struct Edge{
int u,v,w;
}e[maxn];
bool cmp(const Edge&a,const Edge&b){
return a.w<b.w;
}
void init(){
rep(i,1,n) s[i]=i;
}
inline int find(int x){
if(x==s[x]) return x;
int r=x;
while(s[r]!=r) r=s[r];
while(x!=r){
int t=s[x];
s[x]=r;
x=t;
}
return r;
}
inline ll kruskal(){
ll res=0;
sort(e,e+m,cmp);
int cnt=0;
rep(i,0,m-1){
int u=find(e[i].u),v=find(e[i].v);
if(u==v) continue;
s[u]=v;
res+=e[i].w;
cnt++;
if(cnt==n-1) return res;
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
//===========================================================
read(n),read(m);
init();
rep(i,1,m){
read(e[i].u),read(e[i].v),read(e[i].w);
}
write(kruskal());
//===========================================================
return 0;
}
比较
prim算法的复杂度和节点数有关,与边数无关,因此,适合稠密图。
kruskal算法复杂度与边数有关,与节点数无关,因此,适合稀疏图。
3937

被折叠的 条评论
为什么被折叠?



