图的割点 图的割边 二分图

本文深入探讨了阿哈算法中的图的割点、割边及二分图最大匹配等内容,详细解析了割点和割边的查找算法,并通过实例演示了二分图最大匹配的实现过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

枯木逢春不再茂,年少且惜镜边人

这周准备完结阿哈算法,其中有一章的学习不是很熟练(总之学的慢,忘的快。哎,真的,学完得好好从背包开始复习了,等复习完了,准备进行更深层次的学习,也就是从做题中获取知识,淦!像那个男人一样(shy))

首先看看图的割点

割点就是在图中找个点去掉,他的儿子回不去祖先哪里,那么这个点满足割点

给组例子

6 7
1 4
1 3
4 2
3 2
2 5
2 6
5 6

图大概是这样

在这里插入图片描述

#include<stdio.h>
int n,m,e[9][9],root;
int num[9],low[9],flag[9],index;
int min(int a,int b)
{
	return a>b?b:a;
}


void dfs(int cur,int father)
{
	int child=0,i;
	
	index++;
	num[cur]=index;
	low[cur]=index;
	for(i=1;i<=n;i++)
	{
		if(e[cur][i]==1)
		{
			if(num[i]==0)
			{
				child++;
				
				dfs(i,cur);
				
				low[cur]=min(low[cur],low[i]);
				
				if(cur!=root&&low[i]>=num[cur])
				flag[cur]=1;
				if(cur==root&&child==2)
				{
					flag[cur]=1;
				}
			}
			else if(i!=father)
			{
				low[cur]=min(num[i],low[cur]);
			}
			
		}
	}
}


int main()
{
	int i,j,x,y;
	scanf("%d %d",&n,&m);
	for(i=1;i<=n;i++)
	 for(j=1;j<=n;j++)
	 e[i][j]=0;
	 
	 
	 for(i=1;i<=m;i++)
	 {
	 	scanf("%d %d",&x,&y);
	 	e[x][y]=1;
	 	e[y][x]=1;
	 }
	 
	 root=1;
	 dfs(1,root);
	 for(i=1;i<=n;i++)
	 {
	 	if(flag[i]==1)
	 	{
	 		printf("%d",i);
		 }
	 }
	 return 0;
 } 

运行结果大概是这样

在这里插入图片描述

那么简单的说一下,
这个dfs是怎么找呢
首先强调一下,他是通过避开他爸爸,去找路找他爷爷或者他爷爷的爷爷的…
对,是这样的,那么代码中是怎么实现去寻爷的呢
看下面

else if(i!=father)
			{
				low[cur]=min(num[i],low[cur]);

代码中可以看到这个吧
那这个呢就是避免了他碰到他爸爸,如果不是他爸爸,肯定是他爷爷或者…,那么就说明他可以不通过他爸爸回到他爷爷的身边,那么你就算他爸不管他,他爷爷也管他,不会饿死,相反 , 当你走到5号顶点时,儿子就会失道寡助,不知道怎么办,只能面对爸爸的噩梦,因为他想找爷爷没门路,所以他爸爸要是不管他,他就饿死了,他就玩不了LOL,他就成为不了那个男人,他就不能成为世界冠军,他就…对不起,扯远了,那么下面继续讲解

那么这个到底是干什么的呢?

low[cur]=min(low[cur],low[i]);

这个就是既然儿子找到去爷爷的路了 ,爸爸肯定得尾随,然后…对不起又扯远了了(感觉好像儿子去网吧被逮住了一样)

然后呢
就是判断了

那么 怎么判断呢

if(cur!=root&&low[i]>=num[cur])
				flag[cur]=1;
				if(cur==root&&child==2)
				{
					flag[cur]=1;
				}

第一个if:如果他不是根节点那么他如果是割点的话,他儿子能找到的人只能是他爸爸,或者他哥哥,他姐姐,他表姐,他堂姐,他堂…,对不起又扯远了,不错,是不是觉得看起来长,哈哈哈哈,反着就是找不到他爷爷,当然他爷爷对应的值小,所以用大于等于弄,那么如果他是根节点(第二个 if),一个爸爸,没有爷爷,他只有两个儿子
,这两个儿子谁都找不到爷爷,那么 嘿嘿嘿 就只能乖乖写作业了

如果他是两个结点,那么这个时候,你就会问:
咱两谁爹谁儿子啊?
答:你die===我儿子;

额 , 这谁爹谁儿子都分不清

何来爷爷 额 我逻辑乱了到底谁爹谁儿子

好了,下来看看割边

其实只需要将

if(cur!=root&&low[i]>=num[cur])
				flag[cur]=1;

大于等于删除等于,那么就好了
为什么呢?
你想想大于等于是能回到爸爸身边,现在等于都没有了,你回不到爷爷身边,也回不到爸爸身边,那么只能去哥哥姐姐那里了,哎

具体代码如下

#include<stdio.h>
int n,m,e[9][9],root;
int num[9],low[9],flag[9],index;
int min(int a,int b)
{
	return a>b?b:a;
}


void dfs(int cur,int father)
{
	int child=0,i;
	
	index++;
	num[cur]=index;
	low[cur]=index;
	for(i=1;i<=n;i++)
	{
		if(e[cur][i]==1)
		{
			if(num[i]==0)
			{
				child++;
				
				dfs(i,cur);
				
				low[cur]=min(low[cur],low[i]);
				
				if(low[i]>num[cur])
				printf("%d-%d\n",cur,i);
			
			}
			else if(i!=father)
			{
				low[cur]=min(num[i],low[cur]);
			}
			
			
		}
	}
}


int main()
{
	int i,j,x,y;
	scanf("%d %d",&n,&m);
	for(i=1;i<=n;i++)
	 for(j=1;j<=n;j++)
	 e[i][j]=0;
	 
	 
	 for(i=1;i<=m;i++)
	 {
	 	scanf("%d %d",&x,&y);
	 	e[x][y]=1;
	 	e[y][x]=1;
	 }
	 
	 root=1;
	 dfs(1,root);
	 return 0;
 } 

当然特殊情况就是根节点了,大佬们肯定可以很强的

在这里插入图片描述

可以发现变了的只有这一块

for(i=1;i<=n;i++)
	{
		if(e[cur][i]==1)
		{
			if(num[i]==0)
			{
				child++;
				
				dfs(i,cur);
				
				low[cur]=min(low[cur],low[i]);
				
				if(low[i]>num[cur])
				printf("%d-%d\n",cur,i);
			
			}
			else if(i!=father)
			{
				low[cur]=min(num[i],low[cur]);
			}

只是去掉了根节点,为什么呢,因为根节点的话一个儿子,直接切,两个儿子,那么也是随便切,如果两个儿子不相互连接的话

下面看看 二分图最大匹配

二分图是无向图

#include<stdio.h>
int e[101][101];
int match[101];
int book[101];
int n,m;
int dfs(int u)
{
	int i;
	for(i=1;i<=n;i++)
	{
		if(book[i]==0&&e[u][i]==1)
		{
			book[i]=1;
			if(match[i]==0||dfs(match[i]))
			{
				match[i]=u;
				return 1;
			}
		}
	}
	return 0;
}




int main()
{
	int i,j,t1,t2,sum=0;
	
	scanf("%d %d",&n,&m);
	
	
	for(i=1;i<=m;i++)
	{
		scanf("%d %d",&t1,&t2);
		
		e[t1][t2]=1;
	}
	
	for(i=1;i<=n;i++)
	{
		for(j=1;j<=n;j++)
		book[j]=0;
		if(dfs(i))
		{
			
			sum++;
			
		}
		
	}
	
	printf("%d",sum);
	
	return 0;
	
}

在这里插入图片描述

那么是怎么得到的这样的代码的呢?

首先介绍一下匹配

给定一个二分图G,在G的一个子图M中,M的边集{E}中的任意两条边都不依附于同一个顶点,则称M是一个匹配。
极大匹配(Maximal Matching)是指在当前已完成的匹配下,无法再通过增加未完成匹配的边的方式来增加匹配的边数。最大匹配(maximum matching)是所有极大匹配当中边数最大的一个匹配。选择这样的边数最大的子集称为图的最大匹配问题。
如果一个匹配中,图中的每个顶点都和图中某条边相关联,则称此匹配为完全匹配,也称作完备匹配

那么二分图又是什么?

二分图又称作二部图,是图论中的一种特殊模型。 设G=(V,E)是一个无向图,如果顶点V可分割为两个互不相交的子集(A,B),并且图中的每条边(i,j)所关联的两个顶点i和j分别属于这两个不同的顶点集(i in A,j in B),则称图G为一个二分图。
简单的说,一个图被分成了两部分,相同的部分没有边,那这个图就是二分图,二分图是特殊的图。

看一组例子


3  5
1 1
1 2
2 2
2 3
3 1

那么这个图可匹配成这样

1 -1
2 -2
3- -----
或者
1-2
2-3
3-2

显然第二种更对一些那么是怎么得到的呢?

首先
3没有男伴
那么找1 1有了怎么办 ,踢了一号女生
1号女生又找2号男生 2号男生 踢了 2号女生 只能找3号女生 1号男生说算了 和2号女生做吧
3号于是顺利成章的吃到了天鹅肉

那么这个过程是怎么实现的呢?

int dfs(int u)
{
	int i;
	for(i=1;i<=n;i++)
	{
		if(book[i]==0&&e[u][i]==1)
		{
			book[i]=1;
			if(match[i]==0||dfs(match[i]))
			{
				match[i]=u;
				return 1;
			}
		}
	}
	return 0;
}

当然dfs是关键

if(match[i]==0||dfs(match[i]))
			{
				match[i]=u;
				return 1;
			}

这块是判断他是不是匹配过了,或者这个男的(i)的女伴可以找到另一个舞伴,那么这个男的就空出来 供u女伴 那么这样下来,就是最大匹配

好了 今天就到这里了 ,阿哈算法也算基本完了,掌握不牢固,前面留了好多问题,以后开始解决,那么java也就可以开始学习了,但是算法只是入门,还的继续努力。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值