zoukankan      html  css  js  c++  java
  • 二分图的最大匹配、带权最大匹配

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

    Reference:

    google上搜"ByVoid 二分图"(被墙了T^T)

    http://ycool.com/post/cfnym64

    http://segmentfault.com/q/1010000000094642

    http://www.cnblogs.com/kuangbin/archive/2012/08/26/2657446.html

     http://note.dypanda.com/post/2012-08-07/maximal-matching-problem

    计算二分图的最大匹配:匈牙利算法

    模板:

    #include <stdio.h>
    #include <string.h>
    #define MAX 102
    
    long n,n1,match;
    long adjl[MAX][MAX];
    long mat[MAX];
    bool used[MAX];
    
    FILE *fi,*fo;
    
    void readfile()
    {
        fi=fopen("flyer.in","r");
        fo=fopen("flyer.out","w");
        fscanf(fi,"%ld%ld",&n,&n1);
        long a,b;
        while (fscanf(fi,"%ld%ld",&a,&b)!=EOF)
            adjl[a][ ++adjl[a][0] ]=b;
        match=0;
    }
    
    bool crosspath(long k)
    {
        for (long i=1;i<=adjl[k][0];i++)
        {
            long j=adjl[k][i];
            if (!used[j])
            {
                used[j]=true;
                if (mat[j]==0 || crosspath(mat[j]))
                {
                    mat[j]=k;
                    return true;
                }
            }
        }
        return false;
    }
    
    void hungary()
    {
        for (long i=1;i<=n1;i++)
        {
            if (crosspath(i))
                match++;
            memset(used,0,sizeof(used));
        }
    }
    
    void print()
    {
        fprintf(fo,"%ld",match);
        fclose(fi);
        fclose(fo);
    }
    
    int main()
    {
        readfile();
        hungary();
        print();
        return 0;
    }
    View Code

    Exercise: USACO 4.2.2 stall4    模板题

    #include <stdio.h>
    #include <string.h>
    #define MAX 533
    
    long n,n1,match,m,tm,tmp;
    long adjl[MAX][MAX];
    long mat[MAX];
    bool used[MAX];
    /*
    void readfile()
    {
        scanf("%ld%ld",&n,&n1);
        long a,b;
        while (scanf("%ld%ld",&a,&b)!=EOF)
            adjl[a][ ++adjl[a][0] ]=b;
        match=0;
    }
    */
    
    void readfile()
    {
        memset(adjl,0,sizeof(adjl));
        scanf("%ld%ld",&n,&m);              //1..n:cow  n+1..n+m:stall
        n1=n+m;
        for (int i=1;i<=n;i++)
        {
            scanf("%ld",&tmp);
            adjl[i][0]=tmp;
            for (int j=1;j<=tmp;j++)
            {
                scanf("%ld",&tm);
                adjl[i][j]=tm+n;
                //adjl[tm+n][0]++;
                //adjl[tm+n][adjl[tm+n][0]]=i;
            }
        }
        match=0;
    }
    
    bool crosspath(long k)
    {
        for (long i=1;i<=adjl[k][0];i++)
        {
            long j=adjl[k][i];
            if (!used[j])
            {
                used[j]=true;
                if (mat[j]==0 || crosspath(mat[j]))
                {
                    mat[j]=k;
                    return true;
                }
            }
        }
        return false;
    }
    
    void hungary()
    {
        for (long i=1;i<=n1;i++)
        {
            if (crosspath(i))
                match++;
            memset(used,0,sizeof(used));
        }
    }
    
    int main()
    {
        freopen("stall4.in","r",stdin);
        freopen("stall4.out","w",stdout);
    
        readfile();
        hungary();
        printf("%ld
    ",match);
        return 0;
    }
    View Code

    hdu2063  模板题m个女生和n个男生,用邻接矩阵写的,这样更简单一点。

    WA了好几次,竟然是因为数组没初始化233333

    #include <iostream>
    #include <cstring>
    using namespace std;
    
    int a[555][555];
    bool v[555];
    int mat[555];
    int match,n,m,k,x,y;
    
    bool crosspath(int k)
    {
        for (int j=1;j<=n;j++)
        {
            if (a[k][j]!=0)
            {
                if (!v[j])
                {
                    v[j]=true;
                    if ((mat[j]==0)||(crosspath(mat[j])))
                    {
                        mat[j]=k;
                        return true;
                    }
                }
            }
        }
        return false;
    }
    
    void hungary()
    {
        for (int i=1;i<=m;i++)
        {
            memset(v,0,sizeof(v));
            if (crosspath(i))
                match++;
        }
    }
    
    int main()
    {
        while (cin>>k&&k)        //m:girl    n:boy
        {
            cin>>m>>n;
            memset(a,0,sizeof(a));
            memset(mat,0,sizeof(mat));
    
            for (int i=1;i<=k;i++)
            {
                cin>>x>>y;
                a[x][y]=1;          //a[1..m][1..n]
            }
            match=0;
            hungary();
            cout<<match<<endl;
        }
        return 0;
    }
    View Code

    求带权最大匹配(边上有权值):KM算法

     Reference:

    http://yzmduncan.iteye.com/blog/912044

    ByVoid  二分图带权匹配 KM算法与费用流模型建立

    模板:

    /*其实在求最大 最小的时候只要用一个模板就行了,把边的权值去相反数即可得到另外一个.求结果的时候再去相反数即可*/
    /*最大最小有一些地方不同。。*/
    
    const int maxn = 101;
    const int INF = (1<<31)-1;
    int w[maxn][maxn];
    int lx[maxn],ly[maxn]; //顶标
    int linky[maxn];
    int visx[maxn],visy[maxn];
    int slack[maxn];
    int nx,ny;
    
    bool find(int x)
    {
        visx[x] = true;
        for(int y = 0; y < ny; y++)
        {
            if(visy[y])
                continue;
            int t = lx[x] + ly[y] - w[x][y];
            if(t==0)
            {
                visy[y] = true;
                if(linky[y]==-1 || find(linky[y]))
                {
                    linky[y] = x;
                    return true;        //找到增广轨
                }
            }
            else if(slack[y] > t)
                slack[y] = t;
        }
        return false;                    //没有找到增广轨(说明顶点x没有对应的匹配,与完备匹配(相等子图的完备匹配)不符)
    }
    
    int KM()                //返回最优匹配的值(这个是求最大)
    {
        int i,j;            
        memset(linky,-1,sizeof(linky));
        memset(ly,0,sizeof(ly));
        for(i = 0; i < nx; i++)
            for(j = 0,lx[i] = -INF; j < ny; j++)
                if(w[i][j] > lx[i])
                    lx[i] = w[i][j];            //w[i][j]:distance between point#i and point#j
        for(int x = 0; x < nx; x++)             //i:0..nx-1     j:0..ny-1
        {
            for(i = 0; i < ny; i++)
                slack[i] = INF;
            while(true)
            {
                memset(visx,0,sizeof(visx));
                memset(visy,0,sizeof(visy));
                if(find(x))                        //找到增广轨,退出
                    break;
                int d = INF;
                for(i = 0; i < ny; i++)            //没找到,对l做调整(这会增加相等子图的边),重新找
                {
                    if(!visy[i] && d > slack[i])
                        d = slack[i];
                }
                for(i = 0; i < nx; i++)
                {
                    if(visx[i])
                        lx[i] -= d;
                }
                for(i = 0; i < ny; i++)
                {
                    if(visy[i])
                        ly[i] += d;
                    else
                        slack[i] -= d;
                }
            }
        }
        int result = 0;
        for(i = 0; i < ny; i++)
            result += w[linky[i]][i];
        return result;
    }
    View Code

    Exercise:POJ2195

    #include <iostream>
    #include <cstring>
    using namespace std;
    
    const int maxn = 110;
    const int INF = (1<<31)-1;
    int w[maxn][maxn],h[maxn][maxn],m[maxn][maxn];
    int lx[maxn],ly[maxn];
    int linky[maxn];
    int visx[maxn],visy[maxn];
    int slack[maxn];
    int nx,ny,tx1,tx2,ty1,ty2,tmp,ans,n,k;
    char c;
    
    int abs(int x)
    {
        if (x<0) x=x*(-1);
        return x;
    }
    
    bool find(int x)
    {
        visx[x] = true;
        for(int y = 1; y <= ny; y++)
        {
            if(visy[y])
                continue;
            int t = lx[x] + ly[y] - w[x][y];
            if(t==0)
            {
                visy[y] = true;
                if(linky[y]==-1 || find(linky[y]))
                {
                    linky[y] = x;
                    return true;
                }
            }
            else if(slack[y] > t)
                slack[y] = t;
        }
        return false;
    }
    
    int KM()
    {
        int i,j;
        memset(linky,-1,sizeof(linky));
        memset(ly,0,sizeof(ly));
        for(i = 1; i <= nx; i++)
            for(j = 1,lx[i] = -INF; j <= ny; j++)
                if(w[i][j] > lx[i])
                    lx[i] = w[i][j];
        for(int x = 1; x <= nx; x++)
        {
            for(i = 1; i <= ny; i++)
                slack[i] = INF;
            while(true)
            {
                memset(visx,0,sizeof(visx));
                memset(visy,0,sizeof(visy));
                if(find(x))
                    break;
                int d = INF;
                for(i = 1; i <= ny; i++)
                {
                    if(!visy[i] && d > slack[i])
                        d = slack[i];
                }
                for(i = 1; i <= nx; i++)
                {
                    if(visx[i])
                        lx[i] -= d;
                }
                for(i = 1; i <= ny; i++)
                {
                    if(visy[i])
                        ly[i] += d;
                    else
                        slack[i] -= d;
                }
            }
        }
        int result = 0;
        for(i = 1; i <= ny; i++)
            result += w[linky[i]][i];
        return result;
    }
    
    int main()
    {
        while (cin>>n>>k)
        {
            if ((n==0)&&(k==0)) break;
            nx=0;       ny=0;
            for (int i=1;i<=n;i++)
                for (int j=1;j<=k;j++)
            {
                cin>>c;
                if (c=='H')
                {
                    nx++;
                    h[nx][0]=i;     h[nx][1]=j;
                }
                else if (c=='m')
                {
                    ny++;
                    m[ny][0]=i;     m[ny][1]=j;
                }
            }
            for (int i=1;i<=nx;i++)
                for (int j=1;j<=ny;j++)
                {
                    tx1=h[i][0];    ty1=h[i][1];
                    tx2=m[j][0];    ty2=m[j][1];
                    tmp=abs(tx2-tx1)+abs(ty2-ty1);
                    w[i][j]=tmp*(-1);
                }
    
            ans=KM();   ans=ans*(-1);
            cout<<ans<<endl;
        }
    
    }
    View Code
  • 相关阅读:
    hdu 4015找规律
    hdu4473
    hdu 4016搜索
    hdu4465精度控制
    hdu 2965组合数学题
    hdu 4022map和list等数据结构的应用
    hdu4464超级大水题
    在ASP.NET中实现Url Rewriting
    DotText源码阅读(1)调试
    DotText源码阅读(2)数据库表结构
  • 原文地址:https://www.cnblogs.com/pdev/p/3879954.html
Copyright © 2011-2022 走看看