prim算法+ Kruskal算法+匈牙利算法(超详细)

算法思路:

(1)初始化所有点到集合的距离为正无穷;

(2)for n次 迭代n次为了把所有点加到树中

                每次找出:不在集合内 && (第一次走||不在集合内的最小点)

                用最小点t更新所有点到集合的最小距离

                最后把最小点t加入到集合内(标记为TRUE)

代码:

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cstring>
#define N 510
#define INF 0x3f3f3f3f
using namespace std;
int n,m;
int g[N][N];//存图 
int dist[N];//存点到集合的距离
bool st[N];

int prim()
{
	int res=0;//最小生成树的边权和 
	memset(dist,0x3f,sizeof dist);
	for(int i=0;i<n;i++)//迭代n次,选完所有点 
	{
		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)
		//不是第一次而且所选的最小距离都是INF,不存在最小生成树 
			return INF;
		if(i)//不是第一次选的无穷点,就累加路径
			res+=dist[t];
		for(int j=1;j<=n;j++)//用t更新所有点到集合的最小距离 
			dist[j]=min(dist[j],g[t][j]);
		st[t]=true;//加到集合中 
	} 
	return res; 
}
int main()
{
	cin>>n>>m;
	memset(g,0x3f,sizeof g);
	while(m--)
	{
		int a,b,c;
		cin>>a>>b>>c;
		g[a][b]=g[b][a]=min(g[a][b],c);//图是无向边 
		//防止多条边,求出最短边 
	}
	int t=prim();
	if(t == INF) puts("impossible");
    else cout << t << endl;
	return 0;
 } 

 Kruskal算法求最小生成树

算法思路:

(1)把所有边按照权值从小到大排序

(2)从小到大遍历边,如果边的两个点没有连接,就把他们用并查集连起来

(3)如果a的祖先和b的祖先一样,说明这两个点已经连过了,再连会产生回环。

#include<iostream>
#include<algorithm>
#define N 200010
using namespace std;
int n,m;
int p[N];
struct edge{
	int a,b,w;
	bool operator < (const edge &W)const 
	{//当结构体用<时规则按照w来比 
		return w<W.w;
	} 
}edges[N];
 
int find(int x)
{
	if(p[x]!=x)
		p[x]=find(p[x]);
	return p[x];
}
int main()
{
	cin>>n>>m;
	for(int i=0;i<m;i++)
	{
		int a,b,w;
		cin>>a>>b>>w;
		edges[i]={a,b,w};
	} 
	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[b]=a;//ab相连 
			res+=w;//记录最小生成树的边权和 
			cnt++; //边数量++ 
		}
	}
	if(cnt<n-1)//边小于结点数-1就不能生成 
		cout<<"impossible"<<endl;
	else
		cout<<res<<endl; 
	return 0;
}

二分图:(把所有点划分到两边,两集合内都有边)

(1)二分图当且仅当图中不含奇数环(边数为奇数)

染色法:

开始对任意一未染色的顶点染色。

判断其相邻的顶点中,若未染色则将其染上和相邻顶点不同的颜色。

若已经染色且颜色和相邻顶点的颜色相同则说明不是二分图,若颜色不同则继续判断。

bfs和dfs可以搞定!

DFS:

#include<stdio.h>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 100010
#define M 200010
int e[M],ne[M],h[N],idx;
//int st[N];
int color[N];
void add(int a,int b)
{
	e[idx]=b;
	ne[idx]=h[a];
	h[a]=idx++;
}
bool dfs(int u,int c)
{
	color[u]=c;//u点染c
	for(int i=h[u];i!=-1;i=ne[i]) 
	{
		int b=e[i];//下一个点 
		if(!color[b])//没染过色就去染 
		{
			if(!dfs(b,3-c))//染过之后出问题了就f 
				return false;
		}
		else if(color[b]&&color[b]!=3-c)//染过了但是不是另一种颜色 
			return false;
	}
	return true;
}
int main()
{
	int n,m;
	cin>>n>>m;
	memset(h,-1,sizeof h);
	while(m--)
	{
		int a,b;
		cin>>a>>b;
		add(a,b);
		add(b,a);//无向图 
	}
	bool flag=true;
	for(int i=1;i<=n;i++)
	{
		if(!color[i])//未染色的话 
		{
			if(!(dfs(i,1)))//递归和他相邻点 
			//如果递归该点的过程中有矛盾就no
			{
				cout<<"No"<<endl;
				return 0;
			} 
		}
	 } 
	 cout<<"Yes"<<endl;
	return 0;
} 

BFS:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#define N 100010
#define M 200010
#include<queue>

using namespace std;
typedef pair<int, int>PII;
int e[M],ne[M],h[N],idx;
int n,m;
int st[N];

void add(int a,int b){
	e[idx]=b;
	ne[idx]=h[a];
	h[a]=idx++;
}

bool bfs(int u)
{
	queue<PII> q;
	q.push({u,1});
	st[u]=1;
	while(!q.empty())
	{
		PII t;
		t=q.front();
		q.pop();
		int ver=t.first;
		int col=t.second;
		for(int i=h[ver];i!=-1;i=ne[i]){
			int j=e[i];
			if(!st[j])
			{
				st[j]=3-col;
				q.push({j,3-col});
			}
			else if(st[j]==col)
				return false;
		}
	}
	return true;
}
int main(){
    scanf("%d%d", &n, &m);

    memset(h, -1, sizeof h);
    while(m --){
        int a, b;
        scanf("%d%d", &a, &b);
        add(a, b), add(b, a);
    }

    int flag = true;
    for(int i = 1; i <= n; i ++) {
        if (!st[i]){
            if(!bfs(i)){
                flag = false;
                break;
            }
        }
    }
    if (flag) puts("Yes");
    else puts("No");
    return 0;
}

 匈牙利算法:(最大匹配算法)

两个区间求最大匹配数

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#define N 510
#define M 100010

using namespace std;
bool st[N];//模拟匹配的时候用 
int h[N],ne[M],e[M],idx;
int n1,n2,m;
int match[N];//女孩i现在匹配的男友 

void add(int a,int b)
{
	e[idx]=b;
	ne[idx]=h[a];
	h[a]=idx++;
}
void init()
{
	memset(h,-1,sizeof h);
}
int find(int x)
{
	for(int i=h[x];i!=-1;i=ne[i])
	{
		int j=e[i];
		if(!st[j])//该轮模拟匹配女孩没有被预定 
		{
			st[j]=true;//先预定再说,看看其原配怎么说 
			if(!match[j]||find(match[j])) 
			{//女方没有被匹配||他匹配的男友有其他选择 
				match[j]=x;
				return true;
			}
		}
	}
	return false;
}
int main()
{
	init();
	cin>>n1>>n2>>m;
	while(m--)
	{
		int a,b;
		cin>>a>>b;
		add(a,b);
	}
	int cnt=0;//最大匹配数 
	for(int i=1;i<=n1;i++)//左集合数量  
	{//看加入这个男的能不能扩大匹配数 
		memset(st,false,sizeof st);//st是用来当前模拟匹配的 
		if(find(i))
			cnt++;
	}
	cout<<cnt<<endl;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值