zoukankan      html  css  js  c++  java
  • 它们其实都是图(二分图)

    参考《挑战程序设计竞赛》

    https://www.cnblogs.com/Ymir-TaoMee/p/9448406.html

    二分图判定

    • 问题描述:给定一个具有n个顶点的图,要对图上每个顶点染色,并且要使相邻的顶点颜色不同,问是否能最多用2种颜色进行染色。题目保证没有重边和自环。
    • 分析:
      科普:把相邻点染成不同颜色的问题叫做图着色问题。对图进行染色所需要的最小颜色称为最小着色数。最小着色数是2的图称作二分图。
      如果只用2种颜色,那么确定一个顶点的颜色之后,和它相邻的顶点的颜色也就确定了。因此可以用dfs进行遍历,选择任意一个顶点出发,依次确定相邻顶点的颜色,就可以判断是否可以被2种颜色染色了。

     c++解法:

    // 一个简单的二分图的判断
     
    #include <iostream>
    #include <vector>
    #include <cstring>
    using namespace std;
     
    const int MAX_N =105;
    int V,E;
    // 使用邻接表模拟一张无向图
    vector<int> G[MAX_N];
    // 顶点的颜色,初始化为0,上色有两种颜色(0 or 1)
    int color[MAX_N];
     
    bool dfs(int v, int c)
    {
        color[v] = c;       // 把顶点染成c
        for(int i = 0; i < G[v].size(); i++)
        {
            // 如果当前点的相邻的点同色就返回false
            if(color[G[v][i]] == c)
                return false;
            // 如果当前点的邻点还没被染色,就染成-c
            if(color[G[v][i]] == 0 && !dfs(G[v][i], -c))
                return false;
        }
        // 如果当前点都被染过色,就返回true
        return true;
    }
     
    void solve()
    {
        for(int i = 0; i < V; i++)
        {
            if(color[i] == 0)
            {
                if(!dfs(i,1))
                {
                    cout << "no" << endl;
                    return;
                }
            }
        }
        cout << "yes" << endl;
    }
     
    int main()
    {
        cin >> V >> E;
        for(int i = 0; i < E;  i++)
        {
            int s, t;
            cin >> s >> t;
            G[s].push_back(t);
            G[t].push_back(s);  // 如果有向图则无需这一句
        }
        memset(color, 0, sizeof(color));
        solve();
     
        return 0;
    }
    

    Java解法:

    package graph;
    
    import java.util.ArrayList;
    import java.util.Scanner;
    
    
    public class Main {
    	static int MAX_V=10000;
    	static ArrayList<Integer> G[]=new ArrayList[MAX_V];//邻接表存图
    	static int color[]=new int[MAX_V];//顶点的颜色1或-1
    
    	 //把顶点染成1或-1
    	static boolean dfs(int v, int c)
    	{
    		color[v]=c;//把顶点v染成颜色c
    		for (int i=0; i<G[v].size(); i++)
    		{
    			//如果相邻顶点同色,则返回false
    			if (color[G[v].get(i)]==c) return false;
    			//如果相邻顶点还没被染色,则染成-c
    			if (color[G[v].get(i)]==0 && !dfs(G[v].get(i),-c)) return false;//
    		}
    		//如果所有顶点都染过色了,则返回true
    		return true;
    	}
    	public static void main(String[] args) {
    		Scanner sc=new Scanner(System.in);
    		int V,E;
    		V=sc.nextInt();
    		E=sc.nextInt();
    		for (int i = 0; i < MAX_V; i++) {
    			G[i]=new ArrayList<>();
    		}
    		for (int i = 0; i < E; i++) {
    			int s=sc.nextInt();
    			int t=sc.nextInt();
    //			G[s]=new ArrayList<>(); 不能这样写 这样会重复初始化
    //			G[t]=new ArrayList<>();
    			G[s].add(t);
    			G[t].add(s);
    		}
    		for (int i=0; i<V; i++)
    		{
    			if (color[i]==0)
    			{
    				if (!dfs(i,1))//如果顶点还没被染色,则染成1
    				{
    					System.out.printf("No
    ");
    					return;
    				}
    			}
    		}
    		System.out.printf("Yes
    ");
    	}
    
    }
    

    二分图的最大匹配(匈牙利算法)

    二分图的定义

    如果一个图的所有顶点可以被分为X和Y两个集合,并且所有边的两个顶点恰好一个属于集合X,另一个属于Y,即每个集合内的顶点没有边相连,那么此图就是二分图。

    例图:

    增广路

    找到一条增广路就是使得二分图的配对数加一,增广路的本质是一条路径的起点和终点都是未被配对的点。

    最大匹配

    在当前匹配方案下再也找不到增广路,那么当前匹配就是最大匹配。

    匈牙利算法

      我们来看图二,可以知道了这个二分图各点之间的联系.那么该算法如何实现最大匹配呢.
    2
      根据字典序,显然易见,我们可以知道,A->E.
      随后,我们看B点,它要连E,但是E被占用了,我们该怎么办?我们把A->E之间的边暂时去掉,变成黄色,然后让B->E链接,但是A不能没有,于是这里从A再走,E不行,但是有个F.所以A->F.
    3
    4
      于是C->G也按此法加上,轮到D,我们发现G已经被连了.怎么办?将C->G的边暂时去掉.再从C走,看是否可以找别的边.但是我们发现,C只有到G的一条边.所以C->G保留,D只能孤立一人,我们无能为力.

    算法

    1、首先从任意一个未被配对的点u开始,从点u的边中任意选一条边(假设这条边是u->v)开始配对。如果此时点v还没有被配对,则配对成功,此时便找到一条增广路。此时若点v已经被配对了,那就要尝试进行“回溯”。若尝试成功,则找到一条增广路,此时需要更新原来的配对关系。这里需要一个数组match来记录配对关系,比如点v与点u配对了,就记作match[v]=u和match[u]=v。配对成功后,配对数加一。

    2、若刚才所选的边配对失败,要从点u的边中再重新选一条边,进行尝试。直到点u配对成功,或者尝试过点u所有的边为止。

    3、接下来继续对剩下没有被配对的点一一进行配对,直到所有的点都尝试完毕,找不到新的增广路为止。

    4、输出配对数。
    举一个例子:

    男1 2 3 号,女1 2 3 号 去做过山车。可是,过山车的每一排只有两个座位,为了安全起见,每个女生必须与一名男生同坐。但是,每个女孩都希望和自己认识的人一组,女1只认识男1和男2,女2认识男2和男3,女3认识男1,你可以帮忙算算哪种组合可以坐上过山车吗?

    我们可以这么想,首先从左边的第1号女生开始考虑。先让她与1号男生配对,配对成功后,紧接着考虑2号女生。2号女生可以与2号男生配对,接下来继续考虑3号女生。此时我们发现3号女生只能和1号男生配对,可是1号男生已经配给1号女生了,怎么办?
    此时3号女生硬着头皮走到了1号男生面前,貌似1号男生已经看出了3号女生的来意,这个时候1号男生对3号女生说:“我之前已经答应了与1号女生坐一起,你稍等一下,我让号女生去问问看她能否与其他认识的男生坐一起,如果她找到了别的男生,那我就和你坐一起。”接下来,1号女生便尝试去找别的男生啦。
    此时1号女生来到了2号男生面前问:“我可以和你坐在一起吗?”2号男生说:“我刚答应和2号女生坐一起,你稍等一下,我让2号女生去问问看她能否与其他认识的男生坐起,如果她找到了别的男生,那我就和你坐一起。”接下来,2号女生又去尝试找别的男生啦。此时,2号女生来到了3号男生面前问:“我可以和你坐一起吗?”3号男生说:“我正 空着呢,当然可以啦!”此时2号女生回过头对2号男生说:“我和别的人坐在一起啦。”然 后2号男生对1号女生说:“现在我可以和你坐在一起啦。”接着,1号女生又对1号男生说 “我找到别的男生啦。”最后1号男生回复了3号女生:“我现在可以和你坐在一起啦。”

    最终的结果就是:

    真足波折啊〜〜是不是有点连锁反应的感觉。最终通过这种连锁反应,配对数从原来的2 对变成了3对,增加了 1对。刚才的过程有个专业名词叫做增广路,不难发现如果找到一条增 广路,那么配对数将会加1。增广路的本质就是一条路径的起点和终点都是末被配对的点。 既然增广路的作用是“改进”匹配方案(增加配对数),如果我们己经找到—种匹配方 案,如何确定当前这个匹配方案已经是最大匹配了呢?如果在当前匹配方案下再也找不到增 广路,那么当前匹配就是最大匹配/,算法如下。

    #include<stdio.h>
    int e[100][100],match[100],book[100];
    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;
                    match[u]=i;
                    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;
            e[t2][t1]=1;
        }
        for(i=1;i<=n;i++)
        match[i]=0;
        for(i=1;i<=n;i++)
        {
            for(j=1;j<=n;j++)
            book[j]=0;
            if(dfs(i))
            sum++;
        }
        printf("%d",sum);
        return 0;
    }
    

      

    加油啦!加油鸭,冲鸭!!!
  • 相关阅读:
    执行chmod -R 777 / 补救
    kill详解
    find详解
    htop详解
    C#正则表达式经典分类整理集合手册
    C# 正则表达式大全
    各种新主流.net混淆加密软件对比
    string format double
    System.Timers.Timer
    System.Threading.Timer
  • 原文地址:https://www.cnblogs.com/clarencezzh/p/10372837.html
Copyright © 2011-2022 走看看