工作记录——3

Time:2020.01.16

这是一篇以“工作记录——2”为基础的,实现匈牙利算法的工作记录。
程序是参照二分图匹配实现的,他的前提就是已经确定两个集合之间的吸引情况,即0或者1。但是我通过几何距离算法得到的是两个集合之间的距离,所以我主观的将最小的和次之的数设置为1,其余的设置为0。总感觉这种方法带入了主观判断,有一些缺陷,因此接下来要考虑对数组采用“指派法”或者“回溯法”实现。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <string.h>
using namespace std;

#define MAXN 990                  //MAXN表示X集合和Y集合顶点个数的最大值
int nx,ny;                        //x和y集合中顶点的个数
int g[MAXN][MAXN];                //邻接矩阵,g[i][j]为1表示有连接
int cx[MAXN],cy[MAXN];        //cx[i],表示最终求得的最大匹配中,与x集合中元素Xi匹配的集合Y中顶点的索引
                                                        //cy[i],表示最终求得的最大匹配中,与y集合中元素Yi匹配的集合X中顶点的索引

//DFS算法中记录顶点访问状态的数据mk[i]=0表示未访问过,为1表示访问过
int mk[MAXN];

//从集合X中的定顶点u出发,用深度有限的策略寻找增广路
//这种增广路只能是当前的匹配数增加1

int path(int u){
    for(int v=0;v<ny;++v){      //考虑所有Yi顶点v
        if(g[u][v] && !mk[v]){     //Y中顶点v与u邻接,且没有访问过
            mk[v]=1;                        //访问v
            //如果v没有匹配,则直接将v匹配给u,如果v已经匹配了,但是从cy[v],也就是从v之前已经匹配的x出发,找到一条增广路,但是这里记住这里v已经记录访问过了
            //如果第一个条件成立,则不会递归调用
            if(cy[v]==-1 || path(cy[v])){
                cx[u]=v;         //把Y中v匹配给X中u
                cy[v]=u;            //把X中u匹配给Y中v
                return 1;
            }
        }
    }
    return 0;                        //如果不存在从u出发的增广路,则返回0
}

int maxMatch(){        //求二分图最大匹配的匈牙利算法
    int res=0;
    memset(cx,-1,sizeof(cx));        //从0匹配开始增广,将cx和xy各元素都初始化为-1
    memset(cy,-1,sizeof(cy));
    for(int i=0;i<nx;++i){
        if(cx[i]==-1){                                    //从X集合中每个没有匹配的点出发开始寻找增广路
            memset(mk,0,sizeof(mk));
            res+=path(i);
        }
    }
    return res;
}

int main() {
    nx=5;
    ny=5;
    int b=120,a=50;
    vector<vector<double>> H(nx,vector<double>(ny));
 // vector<vector<double>> g(nx,vector<double>(ny));  //这里与起始的g冲突了,因此一直实现错误,找不到可以匹配的对象,因此在这里把其注释掉,且其类型可改成int型,即使用起始位置定义的g[][]
    double array[ny];
    for(int i=0;i<nx;i++){
        for(int j=0;j<ny;j++){
            H[i][j]=0.01+(rand()%(b-a+1)+a);
            cout<<"H["<<i<<"]"<<"["<<j<<"]"<<H[i][j]<<endl;
            array[j]=H[i][j];
        }
        double m1,m2;//存储两个最小值
        m1=9999;
        m2=9999;
        for(int k=0;k<ny;k++){
            if(array[k]<m1){
                m2=m1;
                m1=array[k];
            }
            else if(array[k]<m2){
                m2=array[k];
            }
        }
       for(int l=0;l<ny;l++){
           if(array[l]==m1){
               //array[l]=1;
               g[i][l]=1;
           }
           else if(array[l]==m2){
               //array[l]=1;
               g[i][l]=1;
           }
           else{
               g[i][l]=0;
           }
       }
    }

  /* for(int m=0;m<nx;m++){
        for(int n=0;n<ny;n++){
            cout<<"g["<<m<<"]"<<"["<<n<<"]"<<g[m][n]<<endl;
        }
    }
//输出看一下g是否是自己想要的形式
*/
/*
    g[0][0]=1; g[0][1]=1; g[0][2]=0;g[0][3]=0; g[0][4]=0;
    g[1][0]=0; g[1][1]=1; g[1][2]=1;g[1][3]=0; g[1][4]=0;
    g[2][0]=0; g[2][1]=1; g[2][2]=0;g[2][3]=1; g[2][4]=0;
    g[3][0]=0; g[3][1]=0; g[3][2]=1;g[3][3]=1; g[3][4]=0;
    g[4][0]=1; g[4][1]=0; g[4][2]=1;g[4][3]=0; g[4][4]=0;
    g[5][0]=0; g[5][1]=1; g[5][2]=0;g[5][3]=1; g[5][4]=1;
   */
    int num= maxMatch();
    cout<<"num="<<num<<endl;
    for(int num=0;num<ny;++num){
        cout<<"cx["<<num+1<<"] -->> "<<cx[num]+1<<endl;
    }
    return 0;
}

后来根据算法原理,整理的程序。

#define MAXN 990                  //MAXN表示X集合和Y集合顶点个数的最大值
int nx,ny;                        //x和y集合中顶点的个数
int g[MAXN][MAXN];                //邻接矩阵,g[i][j]=1表示有连接
int cx[MAXN],cy[MAXN];            //cx[i],表示最终求得的最大匹配中,与x集合中元素Xi匹配的集合Y中顶点的索引
                                  //cy[i],表示最终求得的最大匹配中,与y集合中元素Yi匹配的集合X中顶点的索引
int mk[MAXN];                     //DFS算法中记录顶点访问状态的数据mk[i]=0表示未访问过,为1表示访问过,标记的永远是二分图右边的顶点。

//从集合X中的定顶点u出发,用深度有限的策略寻找增广路
//这种增广路只能是当前的匹配数增加1
int path(int u){                    //传入的是左侧的顶点
    for(int v=0;v<ny;++v){          //考虑所有右侧顶点,从第一个开始扫描,依次向下
        if(g[u][v] && !mk[v]){      //如果左侧有右侧某顶点相连接,并且这个右侧顶点没有被此时的左侧顶点访问过
            mk[v]=1;                //标记右侧顶点,访问v
            //接下来,判断这个右侧顶点。
            //如果v没有匹配,则直接将v匹配给u。或者,如果v已经匹配了,但是从cy[v],也就是从v之前已经匹配的x出发,找到一条增广路,但是这里记住这里v已经记录访问过了
            //如果第一个条件成立,则不会递归调用
            if(cy[v]==-1 || path(cy[v])){     //path()是一个递归更新的过程,其返回值是1或者0;
            //如果满足上述条件,更新cx cy。
                cx[u]=v;            //把Y中v匹配给X中u
                cy[v]=u;            //把X中u匹配给Y中v
                return 1;           //找到左侧点的匹配值,那么将返回1
            }
        }
    }
    return 0;                        //如果不存在从u出发的增广路,则返回0
    //具体的说:如果左侧点找不到一个匹配点,那么将会返回0,此时path(cy[v])=0,就不会将右侧点与之前匹配的点拆散,而是左侧点重新依次向下寻找右侧链接顶点
}

int maxMatch(){                       //求二分图最大匹配的匈牙利算法;
    int res=0;
    memset(cx,-1,sizeof(cx));         //从0匹配开始增广,将cx和xy各元素都初始化为-1;
    memset(cy,-1,sizeof(cy));
    for(int i=0;i<nx;++i){            //开始依次从左侧顶点匹配;
        if(cx[i]==-1){                //从X集合中每个没有匹配的点出发开始寻找增广路;
            memset(mk,0,sizeof(mk));  //每个左侧顶点匹配之前都要初始化;
            res+=path(i);             //输出匹配成功个数,或者if(path(i)) res++
            
        }
    }
    return res;
}

可参考这篇文章:
二分图详解----匈牙利算法+km算法+ Gale-Shapley—婚姻匹配算法算法+例题

原创文章 15 获赞 8 访问量 922

猜你喜欢

转载自blog.csdn.net/weixin_39652282/article/details/104006133
今日推荐