Pentium.Labs

System全家桶:https://zhuanlan.zhihu.com/c_1238468913098731520

导航

二分图的最大匹配、带权最大匹配

给定一个二分图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\n",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

 

posted on 2014-07-31 09:27  Pentium.Labs  阅读(1370)  评论(2编辑  收藏  举报



Pentium.Lab Since 1998