素数伴侣 匈牙利算法

来源:互联网 发布:淘宝双11红包攻略活动 编辑:程序博客网 时间:2024/06/10 07:33

对于0和1来说,它们既不是素数也不是合数。

任何一个大于1的正整数n,可以且唯一表示成有限个素数的乘积。

素数:在一个大于1的自然数中,除了1和此整数自身以外,不能被其他自然数整除的数。

比1大,但不是素数则称之为合数。


匈牙利算法:https://liam0205.me/2016/04/03/Hungarian-algorithm-in-the-maximum-matching-problem-of-bigraph/

增广路径的理解:

理解一:在二分图的匹配中,如果一条路径的首尾是非匹配点,路径中除此之外(如果有)其他的点均是匹配点,那么这条路径就是一条增广路径(Agumenting path)

理解二:

交替路:从一个未匹配点出发,依次经过非匹配边、匹配边、非匹配边…形成的路径叫交替路。

增广路:从一个未匹配点出发,走交替路,如果途径另一个未匹配点(出发的点不算),则这条交替路称为增广路(agumenting path)。


增广路径的首尾是非匹配点。因此,增广路径的第一条和最后一条边,必然是非匹配边;同时它的第二条边(如果有)和倒数第二条边(如果有),必然是匹配边;以及第三条边(如果有)和倒数第三条边(如果有),一定是非匹配边。

亦即,增广路径从非匹配边开始,匹配边和非匹配边依次交替,最后由非匹配边结束。这样一来,增广路径中非匹配边的数目会比匹配边大 1。

1.定义:若无向图G=<V,E>的结点集V能够被划分为两个子集V1,V2,满足V1∩V2=Ø,并且V1UV2=V,使得G中任意一条边的两个端点,一个属于V1,另一个属于V2,则称G为二分图(bigraph)。二分图通常记为G=<V1,E,V2>.

由定义可知,二分图没有自回路;零图,平凡图可以看成是特殊的二分图。

2.定义:在二分图G=<V1,E,V2>中,若V1中的每个结点与V2中的每个结点都有且仅有一条边相关联,则称二分图G为完全二分图,记为Ki,j,其中i=|V1|,j=|V2|.

3.判断是否为二分图的方法:定义法定理:无向图G=<V,E>为二分图的充要条件是G的所有回路的长度均为偶数。

4.在二分图G=<V1,E,V2>中,V1={v1,v2,v3........vq},若存在E的子集E'={(v1,v1'),(v2,v2')........(vq,vq'),其中v1',v2',v3'.......vq'为V2中的q个不同的结点},则称G的子图G'={V1,E',V2}为从V1到V2的一个完全匹配(complete matching)。(需要注意的是,这里对于v2来说并不一定格式一个完全匹配,因为v2中可能不只q个节点!!!)

5.二分图存在完全匹配的判断:

①充要条件-------霍尔定理(Hall定理):二分图G=<V1,E,V2>中存在从V1到V2的匹配的充要条件是V1中任意k个结点至少与V2中的k个结点相邻,k=1,2,3,........|V1|. 通常称为相异性条件(diversity condition)

②充分不必要条件---------定理(t条件)二分图G=<V1,E,V2>如果满足:

(1)V1中每个结点至少关联t条边;

(2)V2中每个结点之多关联t条边。

其中t为正整数,则G存在从V1到V2的匹配。

判断t条件非常简单,只需计算V1中结点的最小度,V2中结点的最大度即可。

③必要条件---------|V1|<=|V2|



无向图G为二分图的充分必要条件是,G至少有两个顶点,且其所有回路的长度均为偶数。

二部图中两个集合中的节点数量可以不相同!!!

饱和点和匹配:

给定给一个二分图G,M为G边集的一个子集,如果M满足当中的任意两条边都不依附于同一个顶点,则称M为一个匹配。

极大匹配是指在当前已完成的匹配下,无法再通过增加未完成匹配的边的方式来增加匹配的边数。

最大匹配是所有极大匹配中边数最大的一个匹配。

如果一个匹配中,图中的每个顶点都和图中某条边相关联,则称此匹配为完全匹配,也称作完备匹配。

若图中顶点vi与M中的边相关联,则称vi是M饱和点,否则称为非饱和点。


Hall定理:http://www.cnblogs.com/zxfx100/archive/2012/07/26/2609704.html这篇文章对Hall定理以及证明有很详细的讲解!!!


匈牙利算法可以用深度遍历以及广度遍历来实现:

https://my.oschina.net/husthang/blog/840806

https://www.byvoid.com/zhs/blog/hungaryZ这是大神写的,里面对匈牙利算法用画图的方式进行了生动的解析,非常好,可以关注一下!!参考这个博客以及word里面的

程序可以很好的理解匈牙利算法!!!


#include <iostream>
#include <vector>
#include <string.h>
#include <stdio.h>
using namespace std;
  
  
    bool isPrime(int x){//判断x是否为质数
        if(x<2)
            return false;
        if(x==2)
            return true;
        for(int i=2;i<x;i++){
            if(x%i==0)
                return false;
        }
        return true;
    }
    bool dfs(int k,int* match,int** adj,int* visited,vector<int>& even){//length代表是even的长度
        int t;
        int temp;
        int length=even.size();
        for(int i=0;i<length;i++){
            temp=even[i];
            if(adj[k][temp]==1&&visited[i]==0){
                visited[i]=1;
                t=match[i];
                match[i]=k;
                if(t==-1||dfs(t,match,adj,visited,even))
                    return true;
                match[i]=t;
                  
            }
        }
        return false;
     
    }//dfs函数只对匹配过的odd中的点有影响,如果之前odd中的s是没有被匹配过的,那么
//执行完这次的dfs之后也不会对s产生任何影响,它也不会变成匹配过的点
int main(){
     
    int N;
    cin>>N;//输入自然数个数
    while(N%2!=0){
        cin>>N;//保证输入的N为偶数
    }
    int* number=new int[N];
    for(int i=0;i<N;i++){
        cin>>number[i];
    }
    //先创建邻接表,相加为素数则邻接表中的值为1
    int** adj=new int*[N];
    for(int r=0;r<N;r++){
        adj[r]=new int[N];
    }
    int temp;
    for(int i=0;i<N;i++){
        for(int j=0;j<N;j++){
            temp=number[i]+number[j];
            if(isPrime(temp)&&(i!=j))
                adj[i][j]=1;//adj中的下标对应number中的下标
            else
                adj[i][j]=0;
        }
    }
    //将number中的数据分成奇数和偶数
    vector<int> odd;//奇数
    vector<int> even;//偶数
    for(int k=0;k<N;k++){
        if(number[k]%2==0)
            even.push_back(k);//这里面放的是number下标
        else
            odd.push_back(k);//这里面放的是number下标
    }
      
    // 初始化数组visited,表示even中的点i是否被访问过
    int length=even.size();
    int* visited=new int[length];
    for(int jj=0;jj<length;jj++){
        visited[jj]=0;
    }
      
      
    //初始化数组match,表示数组even中的点i的匹配点,初始化为-1
    int* match=new int[length];
    for(int kk=0;kk<length;kk++){
        match[kk]=-1;
    }
      
    //下面是对odd和even中的点进行最大匹配算法
    int sum=odd.size();
    int count1=0;
    for(int p=0;p<sum;p++){
           memset(visited,0,sizeof(int)*length);//这里一定要将visited清零
           if(dfs(odd[p],match,adj,visited,even)){
               count1=count1+1;
           }
    }
    cout<<count1<<"\n";
      
    //注意这里不可以用final作为变量名,这样的话是无法cout的!!
    delete number;
    for(int t=0;t<N;t++){
        delete[] adj[t];
    }
    delete[] adj;
    delete[] visited;
    delete[] match;
    return 0;
}

上面的程序实现用到了一个重要性质就是:素数,除了2是偶数,其他是奇数。并且,2个数的和是奇数的情况只有奇数+偶数。


 饱和      

原创粉丝点击