图论

1.图论基础

dfs(深度优先遍历)

——图的遍历——
这道题对于时间复杂度要求比较高,因此用常规做法会导致TLE,因此这道题有一个比较巧妙的优化方法,本题要求的是本点能遍历到的最大的值,正常思路是我们一个一个点找,找到最大的值。但是我们还有一个思路,就是从1,2,3,……n中反向建图,倒着查找,具体如何实现的呢?
首先n作为所有点中最大的点,反向建图,在有向图中所有能连到n这个点的点,答案都作为n,再依次遍历n-1……直到1(还需要一个布尔数组来标记有没有访问过),这样就有效的降低时间复杂度。

#include<stdio.h>
#include<iostream>
#include<cstring>
using namespace std;
bool st[101010];
int ans[1010100]={0};
int idx,h[100100],e[101010],ne[101010];
void add(int a,int b){
	e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void dfs(int u,int s){
	if(ans[u]) return ;
	ans[u]=s;//把所有能和u连接起来的点,答案均标记为s
	for(int i=h[u];i!=-1;i=ne[i]){
		int p=e[i];
		dfs(p,s);		
	}
	return ;
}
int main(){
	memset(h,-1,sizeof(h));
	int n,m;
	cin>>n>>m;
	while(m--){
		int a,b;
		scanf("%d %d",&a,&b);
		add(b,a);
	}
	for(int i=n;i>0;i--){
		dfs(i,i);	
	} 
	for(int i=1;i<=n;i++)printf("%d ",ans[i]);
	return 0;
}

树的重心题目传送门acwing

首先强调一下树的重心的定义(因为下面一道题需要使用到树的重心)

重心定义:重心是指树中的一个结点,如果将这个点删除后,剩余各个连通块中点数的最大值最小,那么这个节点被称为树的重心。

树的重心这道题也就是让你去找挖去这个点以后的所有连通块的最大节点数最小的位置就可以,这道题应该和下面的一道题结合来看。

#include<stdio.h>
#include<iostream>
#include<cstring>
using namespace std;
int ne[1010010];
int e[1010010];
int h[1010010];
int idx;
int ans=1010010;
int n;
bool st[1001010];
//int res;
int add(int a,int b){
	e[idx]=b,ne[idx]=h[a],h[a]=idx++;
} 
int dfs(int u){
    int res=0; 
	st[u]=true;
	int sum=1;
	for(int i=h[u];i!=-1;i=ne[i]){
		if(!st[e[i]]){
			int s=dfs(e[i]);
			sum+=s;
			res=max(s,res);
		}
	} 
	res=max(res,n-sum);
	ans=min(res,ans);
	return sum;	
} 
int main(){
	cin>>n;
	memset(h,-1,sizeof(h));
	for(int i=1;i<n;i++){
		int a,b;
		cin>>a>>b;
		add(a,b);
		add(b,a);
	}
	dfs(1);
	cout<<ans<<endl;
	return 0;
}

会议(找树的重心)题目传送门

#include<iostream>
#include<cstring>
#include<map> 
using namespace std;
int e[10101000],ne[10101101],idx,h[10110010];
int n;
int g[1010110];//这个点的全体子树的节点数目和
bool st[1011000];
int f[1010101];//全体res的集合
int ans;
int ansd;
void add(int a,int b){
	e[idx]=b;
	ne[idx]=h[a];
	h[a]=idx++;
}
int pr(int u){
	int ans=0;
	for(int i=1;i<=u;i++){
		ans+=i;
	}
	return ans;
}
int dfs(int u){
	int res=0;
	int sum=1;
	st[u]=true;
	for(int i=h[u];i!=-1;i=ne[i]){
		int j=e[i];
		if(!st[j]){
			int s=dfs(j);
			g[u]+=s;//因为在这里计算的最终结果因此不会把这个点sum=1算进去duck放心
			sum+=s;
			res=max(s,res);
		}
	}
	res=max(res,n-sum);
	f[u]=res;
	return sum;
}
int main(){
	memset(h,-1,sizeof h);
	cin>>n; 
	for(int i=1;i<n;i++){
		int a,b;
		cin>>a>>b;
		add(a,b);
		add(b,a);//无向图存图
	}
	dfs(1);
	int app=0x3f3f3f3f;
	int y;
	for(int i=1;i<=n;i++){
		if(app>f[i])	app=f[i],y=i;
	}
	printf("%d ",y);
	memset(g,0,sizeof(g));
	memset(st,0,sizeof(st));
	dfs(y);
	for(int i=1;i<=n;i++){
	ans+=g[i];
	}
		
	printf("%d",ans);
	return 0;
}

题目中的res都是挖去这个拟定点以后求得的连通块中的最大的节点数,最后对于全体res我们求的最小值就是我们所要求的树的重心,
第一题我们要求的就是res的最小值,直接输出就可以。
第二题要求求的结果是重心节点的编号,和所有节点到我们重心节点的距离和,那就先把整张图先搜索出重心节点,对于求所有点到重心节点的距离和那么就找到以这个点为整张图的根节点。
那么第一步就是把整张图当成无根树,进行遍历找出我们的树的重心,(也就是深度优先遍历找到我们应该找到的连通块中的最大根节点的最小值)(第一题作为我们第二题的第一步)。把所有的res按照u存起来,之后遍历找出最小的u,便是我们应该找到的树的重心的那个点。
第二步:就是把那个点作为重心去dfs(y),这个的所有子树和便是我们得到的答案,为什么呢?
下面有图有真相。(蒟蒻不会调整方向我还是太菜了)
在这里插入图片描述
根据递归,每一次遍历,从末端本图是(1.4)开始传递sum,对于(3,4)来说是传递两次因此分别是1,2(累加效应),正好是3到2的距离,和4到2的距离,又因为每一个边的边权都是1,那么这里根据累加效应,那么所有的g[i]的和便是最后的答案,也就是答案就是我们要求这个点的所有子树和。

在这里插入图片描述
那么这个图也就容易解了
在这里插入图片描述

bfs(宽度优先遍历)

一般来说我们认为存图可以使用链表,(数组模拟的链表),但是数组模拟的链表其实不容易访问,下面一道题可能就不会那么好做。
——查找文献——
这道题如果题面中没有说我需要排序的话,那么链表实现也没有那么复杂,但是我们需要对每一次的查找到的点的所有连接点(bfs)(dfs)排序!那么用数组实现的链表就无从下手,因此我们使用二维不定数组来存储这个图,这样,排完序后对于每一个点我们都会优先查找数据较小的点。
这道题主要我认为是存图方式和我们一般的链表不一样,是个模板题,其实不是很难。但是作为排序,在题目中还是比较麻烦的。

#include<iostream>
#include<stdio.h>
#include<cstring>
#include<queue>
#include<map>
#include<algorithm>
#include<vector>
using namespace std;
queue<int>q;
vector<int>a[1010100];//二维不定数组
bool sdfs[10101010],sbfs[10101010];
void dfs(int u){
	printf("%d ",u);
	sdfs[u]=true;
	for(int i=0;i<a[u].size();i++){
		if(!sdfs[a[u][i]]){
			sdfs[a[u][i]]=true;
			dfs(a[u][i]);
		}
	}
	return ;
}
void bfs(int u){
	printf("%d ",u);
	sbfs[u]=true;
	q.push(u);
	while(q.size()){
		int p=q.front();
		q.pop();
		for(int i=0;i<a[p].size();i++){
			int o=a[p][i];
			if(!sbfs[o]){
			sbfs[o]=true;
			q.push(o);
			printf("%d ",o);	
			}
			
		}
	}
	return ;
}
int main(){
	int n,m;
	cin>>n>>m;
	for(int i=0;i<m;i++){
		int x,y;
		cin>>x>>y;
		a[x].push_back(y);//存图,x为行
	}
	for(int i=1;i<=n;i++)	sort(a[i].begin(),a[i].end());//这样存储主要是为了好排序
	dfs(1);
	printf("\n");
	bfs(1);
	return 0;
}

bfs和拓扑排序

——最大食物链级数——

题目背景
你知道食物链吗?Delia 生物考试的时候,数食物链条数的题目全都错了,因为她总是重复数了几条或漏掉了几条。于是她来就来求助你,然而你也不会啊!写一个程序来帮帮她吧。
题目描述
给你一个食物网,你要求出这个食物网中最大食物链的数量。
(这里的“最大食物链”,指的是生物学意义上的食物链,即最左端是不会捕食其他生物的生产者,最右端是不会被其他生物捕食的消费者。)
Delia 非常急,所以你只有 11 秒的时间。
由于这个结果可能过大,你只需要输出总数模上 8011200280112002 的结果。
输入格式
第一行,两个正整数 n、m,表示生物种类 n 和吃与被吃的关系数 mm。
接下来 m 行,每行两个正整数,表示被吃的生物A和吃A的生物B。
输出格式
一行一个整数,为最大食物链数量模上 8011200280112002 的结果。

本题是一个拓扑排序,拓扑排序就是

对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边<u,v>∈E(G),则u在线性序列中出现在v之前。通常,这样的线性序列称为满足拓扑次序(Topological Order)的序列,简称拓扑序列。简单的说,由某个集合上的一个偏序得到该集合上的一个全序,这个操作称之为拓扑排序。

拓扑排序必须是DAG!由概念我们也可以知道入度越高,位置越靠后,入度越低位置越靠前。
那么本题和拓扑排序有什么联系呢?
作为食物链来说,食物链中的生产者是入度为0的,只有出度,而最高级消费者是只有入度没有出度,比方说森林之王,只有它吃别的小动物,没有别的小动物吃它(正常情况下),那么它就是在食物链最右端。5是最高级消费者1是生产者
如图,食物链是从生产者指向最高级消费者(旧版高中生物必修3hhh),从1到5的路径数等于从2到从1到234的路径之和,那么让初始的点是答案是1,让1这个点到23分别有俩支路那么2和3分别有了从1带来的路径,接着把1删掉,这样2就成为了新的生产者,从2方向带来的一条路经,分别可以到3和5,这样3就有了2条,5有了一条,接着3成了生产者以此类推……最后我们只需要输出所有出度为0的点的路径和就好了1。本题非常好,我觉得可以反复去做一做。

#include<iostream>
#include<cstring>
using namespace std;
typedef long long ll;
int ans[10101011];
int q[10100100];
int to[10100101],in[10101010];
int idx,e[10100101],ne[10100011],h[10100101];
void add(int a,int b){
	e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
int hh=0,tt=-1;
void topo(){
	while(hh<=tt){
		int p=q[hh++];
	for(int i=h[p];i!=-1;i=ne[i]){
		int s=e[i];
		ans[s]=(ans[s]+ans[p])%80112002;
		in[s]--;//删点操作
		//这一步操作就是让下面的点入度-1,入度为0的点作为生产者压入栈中
		if(!in[s]){
			q[++tt]=s;
	}
	}
    }
}
int main(){
	int n,m;
	cin>>n>>m;
	memset(h,-1,sizeof(h));
	while(m--){
		int a,b;
		cin>>a>>b;
		add(a,b);
		to[a]++;in[b]++;
	}
	for(int i=1;i<=n;i++){
		if(!in[i]){
			q[++tt]=i;
			ans[i]=1;
		}
	}
	topo();
	ll quw=0;
	for(int i=1;i<=n;i++)if(!to[i])quw=(quw+ans[i])%80112002;
	cout<<quw<<endl;
	return 0;
}

可以再看一道有关拓扑排序的问题

——最长路——

题目描述
设 G 为有 n 个顶点的带权有向无环图,G 中各顶点的编号为 1 到 n,请设计算法,计算图 G 中 <1,n> 间的最长路径。
输入格式
输入的第一行有两个整数,分别代表图的点数 n 和边数 m。
第 2 到第 (m + 1) 行,每行 3 个整数 u, v,w,代表存在一条从 u 到 v 边权为 w 的边。
输出格式
输出一行一个整数,代表 1 到 n 的最长路。
若 1 与 n 不联通,请输出 -1。

//生蚝吃人,我很抱歉
#include<iostream>
#include<vector>
#include<cstring>
#include<queue>
#include<cstdio>
using namespace std;
int n,b;
queue<int>q;
vector<int>a[101010];
bool st[1010][1010];
int l[1010][1010];
int in[101010];
int ans[101010];
inline int getint()//这个是读入优化,实测速度比scanf快
{
    char ch;
    int res=0;
    while(ch=getchar(),ch<'0'||ch>'9');
    res=ch-48;
    while(ch=getchar(),ch>='0'&&ch<='9')
    res=(res<<3)+(res<<1)+ch-48;
    return res;
}
void topo(){
	for(int i=2;i<=n;i++){
		if(!in[i]){
			for(int p=0;p<a[i].size();p++)in[a[i][p]]--;
		}
	}
	//上面:对于所有的入度为0的点,我们的1都无法到达,因此应该把这些点
	//删去,当然删点的时候也应该考虑删完以后的影响,所以把所有这个点连
	//接的所有点的入度-1;
	//下面就是正常的BFS
	while(q.size()){
		int u=q.front();
		q.pop();
		for(int i=0;i<a[u].size();i++){
				ans[a[u][i]]=max(ans[u]+l[u][a[u][i]],ans[a[u][i]]);
				in[a[u][i]]--;
				if(!in[a[u][i]]) q.push(a[u][i]);
			}
	}return ;
}
int main(){
	n=getint();
	b=getint();
	while(b--){//这个是存入链表操作;
		int x,y,z;
		x=getint();
		y=getint();
		z=getint();
		l[x][y]=max(z,l[x][y]);
		a[x].push_back(y);
		in[y]++;//这个是入度++;
	}
	q.push(1);//我们要找的是1到终点的最长路
	topo();
	if(ans[n]==0){
		cout<<"-1";
		return 0;
	}
	//没有被赋值的那就是不能联通!因为边权不可能等于0!
	cout<<ans[n];
	return 0;
} 

下面的这一道拓扑排序的问题就相对比较复杂,不是很好做
拓扑排序题目传送门3
这道题题干要求的是只需要你去判断这个能排好序之前的所有情况,也就是说比方说我给100个测试数据,只要前20个就可以把所有的单词排完序,那么我就可以输出我一共比较了多少单词,然后输出所有排好序的之后的,如果说我们排完序之后的单词们在第99个样例和之前的发生逻辑性的错误,对于本道题是不用管的,如果发生错误是在我们排好序之前发生的,那么我们就输出在第几个卡住了,反之输出条件不足。
本道题来说我们应该着手于以下几点:
1.何时发生逻辑性错误
对于排序而言,发生逻辑性错误的可能性只有A>B而且B>A,A和B成环,也就是AB入度相等,没有办法进行排序,那么对于本次排序来说入队的单词数小于我们的现有单词数,那么直接输出错误。
2.何时我们就算排好序了
那么就是保证所有单词都入队的情况下,而且也没有“同级现象发生”,也就是说假设我们只用排好ABCD,但是给出的条件只有A<B,C<D确实所有的单词都入队了,但是A,C同时入度为0,或者说现在知道A<B,A<C,A<D这个在把A删去之后BCD入度同时为0,发生这种情况说明我们现在没有办法排序,应该给予更加充分的条件,因此不能说错误也不能说我们就可以排好序了,所以输出一个两个判断分支都不符合的结果,继续进行下一次的循环。
3.如果发生逻辑性错误和条件不够充分那么应该输出什么?
这个问题就显而易见了,条件不够的情况下发生的逻辑性错误,那么肯定先输出逻辑性错误,因为我们先发现的逻辑性错误,找到逻辑性错误就直接要退出程序,所以你来不及发现数据是不充分的。
这道题是一步一步走,如果你发现可以判断就输出当前的步数,所以是一步一步的拓扑排序,本道题建议多熟悉熟悉。

#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
int n,m;
queue<int>q;
char ans[2102102];
int in[21040521],iin[2102100];
bool st[210];
int e[1021020],ne[1020210],idx,h[1021020]; 
int sum=0;
void add(int a,int b){
	e[idx]=b;
	ne[idx]=h[a];
	h[a]=idx++;
	return;
}
int topo(){
	sum=0;
	int flag=0;
	int ff=0;
	int s=0;//记录本次拓扑排序在正常情况下应该为几个数字进行排序 
	for(int i=1;i<=26;i++){
		iin[i]=in[i];
		if(st[i])s++;
		if(!in[i]&&st[i]){
			if(ff)flag=1;
			ff=1;
			q.push(i);
			ans[++sum]=i;
		}
	}
	ff=0;
	while(q.size()){
		int x=q.front();
		//printf("x=%d\n",x);
		q.pop();
		ff=0;
		for(int i=h[x];i!=-1;i=ne[i]){
			int u=e[i];
			iin[u]--;
			if(!iin[u]){
				if(ff)flag=1;
				ff=1;
				q.push(u);
				ans[++sum]=u;
			}
		}
	}
	//printf("%d %d\n",sum,s);
	if(sum!=s)return -1;//表示错误
	if(flag)return 0;//表示还应该继续排序
	return 1;//表示可以输出了 
	
}
int main(){
	cin>>n>>m;
	memset(h,-1,sizeof h);
	char a,b,pio;
	for(int o=1;o<=m;o++){
		getchar();
		cin>>a>>pio>>b;
		add(a-64,b-64);
		in[b-64]++;
		st[a-64]=true;
		st[b-64]=true;
		//printf("true=%d %d\n",a-64,b-64);
		int t=topo();//记录返回值 
		if(sum==n&&t==1){
			printf("Sorted sequence determined after %d relations: ",o);
			for(int i=1;i<=n;i++){
				printf("%c",ans[i]+64);
			}
			printf(".");
			return 0;
		}
		if(t==-1){
			printf("Inconsistency found after %d relations.",o);
			return 0;
		}
	}
	printf("Sorted sequence cannot be determined.");
	return 0;
} 

当然本道题还有相对来说极为相似的题目(简化版本)可以一试
拓扑排序题目传送门4

欧拉回路

2.最短路算法

优先队列优化的dijkstra算法

所谓堆优化算法就是运用C++ STL库中的优先队列进行算法时间复杂度的优化,下面是代码的模板框架。

typedef pair<int, int> PII;

int n;      // 点的数量
int h[N], w[N], e[N], ne[N], idx;       // 邻接表存储所有边
int dist[N];        // 存储所有点到1号点的距离
bool st[N];     // 存储每个点的最短距离是否已确定

// 求1号点到n号点的最短距离,如果不存在,则返回-1
int dijkstra()
{
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;
    priority_queue<PII, vector<PII>, greater<PII>> heap;
    heap.push({0, 1});      // first存储距离,second存储节点编号

    while (heap.size())
    {
        auto t = heap.top();
        heap.pop();

        int ver = t.second, distance = t.first;

        if (st[ver]) continue;
        st[ver] = true;

        for (int i = h[ver]; i != -1; i = ne[i])
        {
            int j = e[i];
            if (dist[j] > distance + w[i])
            {
                dist[j] = distance + w[i];
                heap.push({dist[j], j});
            }
        }
    }

    if (dist[n] == 0x3f3f3f3f) return -1;
    return dist[n];
}

关于dijkstra算法只用入队一次的理解就是,第一个点入队,其距离是0,之后是更新这个点所有能到达的点,选择剩下点中较小的点继续入队,也就是说所有能遍历到的点都已经进行了一次最小化运算,所以说对于这个算法我们只用入队一次。
模板题如下
题目传送门

/*#include<iostream>
#include<map>
#include<queue>
#include<cstring>
using namespace std;
typedef pair<int,int>pii;
typedef long long ll;
ll e[1011000],ne[1001000],idx,h[1001000],w[1001010];
ll n,m,k;
bool st[1001000];
ll dis[1000000];
queue<ll>q;
inline ll read(){
    ll x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-')
            f=-1;
        ch=getchar();
    }
    while(ch>='0' && ch<='9'){
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x*f;
}
void add(ll a,ll b,ll c){
	e[idx]=b;
	w[idx]=c;
	ne[idx]=h[a];
	h[a]=idx++;
}
void spfa(){
	memset(dis,0x3f,sizeof dis);
	dis[k]=0;
	st[k]=true;
	q.push(k);
	while(q.size()){
		auto p=q.front();
		q.pop();
		st[p]=false;
		for(ll i=h[p];i!=-1;i=ne[i]){
			ll j=e[i];
			//cout<<j<<"\n";
			if(dis[j]>dis[p]+w[i]){
				dis[j]=dis[p]+w[i];
				if(!st[j]){
					q.push(j);
					st[j]=true;
				}
			}
		}
	}
	return;
}
int main(){
	n=read();
	m=read();
	k=read();
	memset(h,-1,sizeof h);
	for(ll i=1;i<=m;i++){
		ll a,b,c;
		a=read();
		b=read();
		c=read();
		add(a,b,c);
	}
	spfa();
	for(ll i=1;i<=n;i++){
		if(dis[i] >= 0x3f3f3f3f)    printf("2147483647 ") ;
		else printf("%lld ",dis[i]);
	}
	return 0;
}*/
//纪念我死去的SPFA
#include<iostream>
#include<queue>
#include<map>
#include<cstring>
using namespace std;
typedef pair<int,int>pii;
typedef long long ll;
ll e[1010100],ne[1010100],h[1010100],idx,w[1010100];
bool st[1010100];
ll dis[1010100];
ll n,m,k;
inline ll read(){
    ll x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-')
            f=-1;
        ch=getchar();
    }
    while(ch>='0' && ch<='9'){
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x*f;
}
void add(int a,int b,int c){
	e[idx]=b;
	w[idx]=c;
	ne[idx]=h[a];
	h[a]=idx++;
	return;
}
void dijkstra(){
	memset(dis,0x3f,sizeof dis);
	dis[k]=0;
	priority_queue<pii,vector<pii>,greater<pii>>heap;
	//优先队列的定义 
	heap.push({0,k});
	while(heap.size()){
		auto p=heap.top();
		heap.pop();
		
		int u=p.second,up=p.first;
		//问题是堆优化的dijkstra算法只用进入优先队列一次? 
		if(st[u])continue;
		st[u]=true;
		for(int i=h[u];i!=-1;i=ne[i]){
			int j=e[i];
			if(dis[j]>w[i]+up){
				dis[j]=w[i]+up;
				heap.push({dis[j],j});
			}
		}
	}
	return;
}
int main(){
	n=read();
	m=read();
	k=read();
	memset(h,-1,sizeof h);
	for(ll i=1;i<=m;i++){
		ll a,b,c;
		a=read();
		b=read();
		c=read();
		add(a,b,c);
	}
	dijkstra();
	for(int i=1;i<=n;i++){
		if(dis[i]>=0x3f3f3f3f)printf("2147483647 ");
		printf("%lld ",dis[i]);
	}
	return 0;
}

这种题卡SPFA真气人

SPFA算法

3.最小生成树

prim算法(稠密图)

代码框架

int n;      // n表示点数
int g[N][N];        // 邻接矩阵,存储所有边
int dist[N];        // 存储其他点到当前最小生成树的距离
bool st[N];     // 存储每个点是否已经在生成树中


// 如果图不连通,则返回INF(值是0x3f3f3f3f), 否则返回最小生成树的树边权重之和
int prim()
{
    memset(dist, 0x3f, sizeof dist);

    int res = 0;
    for (int i = 0; i < n; i ++ )
    {
        int t = -1;
        for (int j = 1; j <= n; j ++ )
            if (!st[j] && (t == -1 || dist[t] > dist[j]))
                t = j;

        if (i && dist[t] == INF) return INF;

        if (i) res += dist[t];
        st[t] = true;

        for (int j = 1; j <= n; j ++ ) dist[j] = min(dist[j], g[t][j]);
    }

    return res;
}

kruskal算法(稀疏图常用)

代码框架

int n, m;       // n是点数,m是边数
int p[N];       // 并查集的父节点数组

struct Edge     // 存储边
{
    int a, b, w;

    bool operator< (const Edge &W)const
    {
        return w < W.w;
    }
}edges[M];

int find(int x)     // 并查集核心操作
{
    if (p[x] != x) p[x] = find(p[x]);
    return p[x];
}

int kruskal()
{
    sort(edges, edges + m);

    for (int i = 1; i <= n; i ++ ) p[i] = i;    // 初始化并查集

    int res = 0, cnt = 0;
    for (int i = 0; i < m; i ++ )
    {
        int a = edges[i].a, b = edges[i].b, w = edges[i].w;

        a = find(a), b = find(b);
        if (a != b)     // 如果两个连通块不连通,则将这两个连通块合并
        {
            p[a] = b;
            res += w;
            cnt ++ ;
        }
    }

    if (cnt < n - 1) return INF;
    return res;
}

4.二分图

dfs染色法

匈牙利算法

匈牙利算法理论算法复杂度高,但是实际运行效率很赞!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值