图论算法学习笔记

Topological sorting

在这里插入图片描述

//topological sorting
#include<iostream>
using namespace std;
const int N = 5005;
const int M = 500005;
const int mod = 80112002;
int n,m,a,b,en = 0;
struct Edge{
	int next,to;
}edge[M];
int head[N] = {0};
int in[N] = {0},out[N] = {0};
int queue[N] = {0};
int front = 0,back = 0;
bool in_queue[N] = {0};
int f[N] = {0};
long long ans = 0;

void add(int u,int v){
	edge[++en].to = v;
	edge[en].next = head[u];
	head[u] = en;
	in[v] ++;
	out[u] ++;
}

int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++){
		scanf("%d%d",&a,&b);//被吃和吃 
		add(a,b);
	}
	for(int i=1;i<=n;i++){//queue 1base 
		if(in[i]==0) {
			queue[++back] = i;
			f[i] = 1;
		}
	}
	while(back>front){//back==front是空,front没存
		int u = queue[++front]; 
		for(int p=head[u];p;p=edge[p].next){
			int v = edge[p].to ;
			f[v] = (f[v]+f[u])%mod;
			if(in_queue[v]) continue;
			in[v]--;
			if(in[v]==0){
				in_queue[v] = true;
				queue[++back] = v;
			}
		}
	}
	for(int i=1;i<=n;i++){
		if(out[i]==0) ans = (ans+f[i])%mod;
	}
	printf("%d\n",ans);
}

最小生成树Prim

在这里插入图片描述

//MSP 无向图 
#include<iostream>
using namespace std;
const int N = 5e3+5;
const int M = 2e5+5;
const int INF = 0x3f3f3f3f;
int n,m,en = 0,x,y,z;
struct Edge{
	int next,to;
	int val;
}edge[M*2];
int head[N] = {0};
int d[N] = {0}; 
bool in[N] = {0};
long long ans = 0;

void add(int u,int v,int w){
	edge[++en].to = v;
	edge[en].val = w;
	edge[en].next = head[u];
	head[u] = en;
}

void insert(int u){
	in[u] = true;
	ans += d[u];
	for(int p=head[u];p;p=edge[p].next){
		int v = edge[p].to ;
		if(!in[v]) d[v] = min(d[v],edge[p].val);//呃呃这里不加d[u]的
	}
}

int getmin(){
	int minx = 0;
	for(int i=2;i<=n;i++){
		if(in[i]) continue;
		if(!minx) minx = i;
		if(d[minx]>d[i]) minx = i;
	}
	return minx;
}

int main(){
	scanf("%d%d",&n,&m);
	for(int i=2;i<=n;i++){
		d[i] = INF;
	}
	for(int i=1;i<=m;i++){
		scanf("%d%d%d",&x,&y,&z); 
		add(x,y,z);
		add(y,x,z);
	}
	insert(1);
	for(int i=2;i<=n;i++){
		int tmp = getmin();
		if(d[tmp]==INF){
			printf("orz\n");
			return 0;
		}
		insert(tmp);
	}
	printf("%d\n",ans);
	return 0;
} 

可以sort就用kruskal,不可以就prim

最小生成树Kruskal

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int fa[5005],n,m,ans,efrom,eto,cnt;
struct Edge{
    int from,to,w;
}edge[200005];

inline bool cmp(Edge a,Edge b){
    return a.w<b.w;
}
//快排的依据(按边权排序)
inline int find(int x){
    while(x!=fa[x]) x=fa[x]=fa[fa[x]];
    return x;
}

inline void kruskal(){
    sort(edge,edge+m,cmp);
    //将边的权值排序
    for(register int i=0;i<m;i++){
        efrom=find(edge[i].from), eto=find(edge[i].to);//并查集判断环 
        if(efrom==eto){
            continue;
        }
        //若出现两个点已经联通了,则说明这一条边不需要了
        ans+=edge[i].w;
        //将此边权计入答案
        fa[eto]=efrom;
        //将efrom、eto合并
        if(++cnt==n-1){
            break;
        }
        //循环结束条件,即边数为点数减一时
    }
}

int main(){
    scanf("%d%d",&n,&m);
    for(register int i=1;i<=n;i++){
        fa[i]=i;
    }
    //初始化并查集
    for(register int i=0;i<m;i++){
        cin>>edge[i].from>>edge[i].to>>edge[i].w;
    }
    kruskal();
    printf("%d",ans);
    return 0;
}

inline bool cmp要会写,然后就是这里并查集只有路径压缩没有按秩合并,加上也成……
xs,偶尔考试可以用sort,写个cmp就很好办

Kruskal的其他用法:生成完全图

在这里插入图片描述

意思就是你生成的图的最小生成树要是给定的那个树。
思路是对树进行kruskal,每次先访问最小的边,然后进行一些集合的合并,(其实每次访问时两个点一定不是在同一个集合,但是为了计数–集合的大小,我们还是写一个并查集比较方便),合并的时候顺带连上两个集合之间的所有边(除了树边以外都是树边长+1的长度)。
AC代码如下:

#include<iostream>
using namespace std;
const int N = 1e5+5;
int n;
long long ans = 0;
struct Edge{
	int from,to;//不是前向星!! 
	int val;
	Edge &operator =(Edge x){
		from = x.from;
		to = x.to;
		val = x.val;
		return *this;
	}
	bool operator >=(Edge x){
		return val >= x.val;
	}
}edge[N];//kruskal,只存单向 
int fa[N] = {0},siz[N] = {0},dep[N] = {0};

int divide(int low,int high,Edge a[]){
	Edge k = a[low];
	while(low!=high){
		while(low<high && a[high]>=k) high--;
		if(low<high){
			a[low] = a[high];
			low++;
		}
		while(low<high && k>=a[low]) low++;
		if(low<high){
			a[high] = a[low];
			high--;
		}
	}
	a[low] = k;
	return low;
}

void quicksort(int low,int high){
	if(low>=high) return ;
	int mid = divide(low,high,edge);
	quicksort(low,mid-1);
	quicksort(mid+1,high);
	return ;
}

int find(int u){
	if(fa[u]==u) return u;
	return fa[u] = find(fa[u]);
}

void kruskal(){
	for(int i=1;i<n;i++){ 
		int u = edge[i].from ,v = edge[i].to ,w = edge[i].val ;
		if(find(u)==find(v)) continue;
		ans += w;
		ans += (w+1)*((long long)siz[fa[v]]*siz[fa[u]]-1);
		int fu = fa[u],fv = fa[v];//知道写fu fv的好处在哪吗,三个if语句互不干扰,只进去一个,
		//如果写fa[u] fa[v]会改变if括号里面要用到的值,会导致出问题 
		if(dep[fu]<dep[fv]){
			siz[fv] += siz[fu];	fa[fu] = fv;	
		}
		if(dep[fu]>dep[fv]){
			siz[fu] += siz[fv];	fa[fv] = fu;	
		}
		if(dep[fu]==dep[fv]){
			siz[fv] += siz[fu];	fa[fu] = fv; dep[fv]++;
		}
	}
}

int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		fa[i] = i;
		siz[i] = 1;
	}
	for(int i=1;i<n;i++){
		scanf("%d%d%d",&edge[i].from,&edge[i].to,&edge[i].val);
	}
	quicksort(1,n-1);//必须写1~n-1,不能写1~n因为第n条边是0呀
	kruskal();
	printf("%lld\n",ans);
	return 0;
}

注意:

  • kruskal存的时候不必存前向星,因为根本用不上,存个双向反而给排序带来了困惑。
  • 排序不能写bubble,会超时,用quicksort会快一些,但是注意uicksort写的时候传参传1~n-1,不要多传第n条边进去,根本就没有第n条边
  • 按秩合并加了没加一样的
  • 注意中间计算过程也要加(long long),否则会中途爆掉然后WA
  • 笑死了,quicksort还是不会写,复制的之前写的luogu40分代码(因为没用随机数)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值