KM算法(完备匹配下的最大权匹配)

KM算法求的是完备匹配下的最大权匹配: 在一个二分图内,左顶点为X,右顶点为Y,现对于每组左右连接XiYj有权wij,求一种匹配使得所有wij的和最大。


题目:http://acm.hdu.edu.cn/showproblem.php?pid=2255

题意:分房子,对于n个人对n所房子有自己的最大承受价格,要求每个人分到一所房子,并且要求收入最大

输入说明:输入n,代表n个人和n所房子,接下来n行n列代表第i个人对第j所房子的价格

题解:KM算法模板

代码:

#include 
#include 
#define M 310
#define inf 0x3f3f3f3f

int n,nx,ny;
int match[M],lx[M],ly[M],slack[M];
int visx[M],visy[M],mp[M][M];

int DFS(int root)
{
    visx[root] = 1;
    for (int son = 1;son <= ny;son ++)
    {
        if (visy[son])
            continue;
        int gap = lx[root] + ly[son] - mp[root][son];
        if (gap == 0)
        {
            visy[son] = 1;
            if (match[son] == -1||DFS(match[son]))
            {
                match[son] = root;
                return true;
            }
        }
        else if (slack[son] > gap)
            slack[son] = gap;
    }
    return 0;
}
int KM()
{
    int i,j;
    memset (match,-1,sizeof(match));
    memset (ly,0,sizeof(ly));
    for (i = 1;i <= nx;i ++)            //lx初始化为与它关联边中最大的
        for (j = 1,lx[i] = -inf;j <= ny;j ++)
            if (mp[i][j] > lx[i])
                lx[i] = mp[i][j];

    for (int x = 1;x <= nx;x ++)
    {
        for (i = 1;i <= ny;i ++)
            slack[i] = inf;
        while (1)
        {
            memset (visx,0,sizeof(visx));
            memset (visy,0,sizeof(visy));
            if (DFS(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 res = 0;
    for (i = 1;i <= ny;i ++)
        if (match[i] > -1)
            res += mp[match[i]][i];
    return res;
}
int main ()
{
    int i,j;
    while (scanf ("%d",&n)!=EOF)
    {
        nx = ny = n;
        for (i = 1;i <= n;i ++)
            for (j = 1;j <= n;j ++)
                {scanf ("%d",&mp[i][j]);}
        int ans = KM();
        printf ("%d\n",ans);
    }
    return 0;
}

题目: http://acm.hdu.edu.cn/showproblem.php?pid=1533

题意:给定n*m的图,其中‘m’代表一个人,‘H’代表一个房子,要求移动最小的步数可以让所有人都进房子

输入说明:输入 n m,接下来是n*m的图
题解:KM算法模板题

代码:

#include
#include 
#include 
#include
#include
#include
#define M 310
#define inf 0x3f3f3f3f

using namespace std;
char c[150][150];
vector >  house;
vector >  people;

int nx,ny;
int match[M],lx[M],ly[M],slack[M];
int visx[M],visy[M],mp[M][M];

int DFS(int root)
{
    visx[root] = 1;
    for (int son = 1;son <= ny;son ++)
    {
        if (visy[son])
            continue;
        int gap = lx[root] + ly[son] - mp[root][son];
        if (gap == 0)
        {
            visy[son] = 1;
            if (match[son] == -1||DFS(match[son]))
            {
                match[son] = root;
                return true;
            }
        }
        else if (slack[son] > gap)
            slack[son] = gap;
    }
    return 0;
}
int KM()
{
    int i,j;
    memset (match,-1,sizeof(match));
    memset (ly,0,sizeof(ly));
    for (i = 1;i <= nx;i ++)            //lx初始化为与它关联边中最大的
        for (j = 1,lx[i] = -inf;j <= ny;j ++)
            if (mp[i][j] > lx[i])
                lx[i] = mp[i][j];

    for (int x = 1;x <= nx;x ++)
    {
        for (i = 1;i <= ny;i ++)
            slack[i] = inf;
        while (1)
        {
            memset (visx,0,sizeof(visx));
            memset (visy,0,sizeof(visy));
            if (DFS(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 res = 0;
    for (i = 1;i <= ny;i ++)
        if (match[i] > -1)
            res += mp[match[i]][i];
    return -res;
}
int main ()
{
    int i,j;
    int n,m;
    while (~scanf ("%d %d",&n,&m) && n+m!=0)
    {
        for (i = 1;i <= n;i ++)
        {
            getchar();
            for(j=1;j <= m;j++)
            {
                scanf("%c",&c[i][j]);
                if(c[i][j]=='H') house.push_back(make_pair(i,j));
                else if(c[i][j]=='m') people.push_back(make_pair(i,j));
            }
        }
        nx=people.size();
        ny=house.size();
        for(i=0;i


KM算法模板


#define M 310
#define inf 0x3f3f3f3f
//以下是最大权匹配

int nx,ny;//nx代表匹配左项的多少,ny表示匹配右项的多少
int match[M],lx[M],ly[M],slack[M];//match[右项]=所对应的左项
int visx[M],visy[M],mp[M][M];

int DFS(int root)
{
    visx[root] = 1;
    for (int son = 1;son <= ny;son ++)
    {
        if (visy[son])
            continue;
        int gap = lx[root] + ly[son] - mp[root][son];
        if (gap == 0)
        {
            visy[son] = 1;
            if (match[son] == -1||DFS(match[son]))
            {
                match[son] = root;
                return true;
            }
        }
        else if (slack[son] > gap)
            slack[son] = gap;
    }
    return 0;
}
int KM()
{
    int i,j;
    memset (match,-1,sizeof(match));
    memset (ly,0,sizeof(ly));
    for (i = 1;i <= nx;i ++)            //lx初始化为与它关联边中最大的
        for (j = 1,lx[i] = -inf;j <= ny;j ++)
            if (mp[i][j] > lx[i])
                lx[i] = mp[i][j];

    for (int x = 1;x <= nx;x ++)
    {
        for (i = 1;i <= ny;i ++)
            slack[i] = inf;
        while (1)
        {
            memset (visx,0,sizeof(visx));
            memset (visy,0,sizeof(visy));
            if (DFS(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 res = 0;
    for (i = 1;i <= ny;i ++)
        if (match[i] > -1)
            res += mp[match[i]][i];
    return res;//求最小权时取-res,同时图mp[i][j]=-mp[i][j];
}




你可能感兴趣的:(图论------KM算法)