您的位置:首页 > 其它

二分图的最优匹配模版

2015-05-22 16:59 351 查看
二分图的的最优匹配是在求二分图的完备匹配的基础之上求出的, 通过不断的扩展完备匹配,最终达到相等子图。而相等子图的完备匹配就是最优匹配。在此定义了可行顶标。

一个关于最优匹配讲的很好的博客地址:http://www.cnblogs.com/one--world--one--dream/archive/2011/08/14/2138385.html

hdu2255

最大权最优匹配模版:

顶标lx【i】表示与顶点i相连的所有边的最大值; 顶标ly【j】为零(为了保证lx【i】+ly【j】>=w【i】【j】)

当不存在完备匹配时,进行扩展时,顶标lx减去扩展量, 顶标ly加上扩展量

时间复杂度为0(n^4)

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int maxn = 1000 + 10;
int Map[maxn][maxn], n;
int Ls[maxn], Lt[maxn], p[maxn];
bool vs[maxn], vt[maxn];
bool dfs(int u)
{
    vs[u] = true;
    for(int v = 1; v <= n; v++)
    {
        if(Ls[u]+Lt[v]==Map[u][v] && !vt[v])
        {
            vt[v] = true;
            if(p[v]==-1 || dfs(p[v]))
            {
                p[v] = u;
                return true;
            }
        }
    }
    return false;
}
void update()
{
    int a = 1<<30;
    for(int i = 1; i <= n; i++)
        if(vs[i])
            for(int j = 1; j <= n; j++)
                if(!vt[j])
                    a = min(a, Ls[i]+Lt[j]-Map[i][j]);
    for(int i = 1; i <= n; i++)
    {
        if(vs[i])
            Ls[i] -= a;
        if(vt[i])
            Lt[i] += a;
    }
}
void KM()
{
    memset(p, -1, sizeof(p));
    memset(Lt, 0, sizeof(Lt));
    memset(Ls, 0, sizeof(Ls));
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= n; j++)
            Ls[i] = max(Ls[i], Map[i][j]);
    for(int i = 1; i <= n; i++)
    {
        while(1)
        {
            memset(vs, false, sizeof(vs));
            memset(vt, false, sizeof(vt));
            if(dfs(i))
                break;
            else
                update();
        }
    }
    int ans = 0;
    for(int i = 1; i <= n; i++)
        ans += Map[p[i]][i];
    printf("%d\n", ans);
}
int main()
{
    while(~scanf("%d", &n))
    {
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= n; j++)
                scanf("%d", &Map[i][j]);
        KM();
    }
    return 0;
}
时间复杂度为0(n^3)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int inf = 0x3f3f3f3f;
const int maxn = 300 + 10;
int sx[maxn], sy[maxn], vx[maxn], vy[maxn], match[maxn];
int money[maxn][maxn], slack[maxn];
int n;
void chushihua()
{
    memset(sy, 0, sizeof(sy));
    for(int i = 1; i <= n; i++)
    {
        sx[i] = 1;
        for(int j = 1; j <= n; j++)
        {
            scanf("%d", &money[i][j]);
            sx[i] = max(sx[i], money[i][j]);
        }
    }
}
bool dfs(int x)
{
    vx[x] = true;
    for(int i = 1; i <= n; i++)
    {
        int xx= sx[x] + sy[i] - money[x][i];
        if(!vy[i] && !xx)
        {
            vy[i] = true;
            if(match[i]==-1 || dfs(match[i]))
            {
                match[i] = x;
                return true;
            }
        }
        else if(slack[i] > xx)
            slack[i] = xx;
    }
    return false;
}
void bestmatch()
{
    for(int i = 1; i <= n; i++)
    {
        while(1)
        {
            memset(vx, false, sizeof(vx));
            memset(vy, false, sizeof(vy));
            memset(slack, 0x3f, sizeof(slack));
            if(dfs(i))
                break;
            int dd = inf;
            for(int i = 1; i <= n; i++)
                if(!vy[i] && dd>slack[i])
                    dd = slack[i];
            for(int i = 1; i <= n; i++)
            {
                if(vx[i])
                    sx[i] -= dd;
                if(vy[i])
                    sy[i] += dd;
            }
        }
    }
}
int main()
{
    while(~scanf("%d", &n))
    {
        memset(match, -1, sizeof(match));
        chushihua();
        bestmatch();
        int sum = 0;
        for(int i = 1; i <= n; i++)
            sum += money[match[i]][i];
        printf("%d\n", sum);
    }
    return 0;
}


poj2195(此题可以用费用流做)

最小权最优匹配模版:
求最小权最优匹配有两种方案:

(1)把所有的权值取反, 求最大权匹配,得到结果取反

(2)顶标lx【i】表示与顶点i相连的所有边的最小值; 顶标ly【j】为零(为了保证lx【i】+ly【j】<=w【i】【j】)

当不存在完备匹配时,进行扩展时,顶标lx加上扩展量, 顶标ly减去扩展量

#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
using namespace std;
const int maxn = 105;
const int inf = 0x3f3f3f3f;
struct Info
{
    int x, y;
};
char s[105];
int sm[maxn], sh[maxn];
int w[maxn][maxn], n;
int p[maxn];
int lx[maxn], ly[maxn];
bool vx[maxn], vy[maxn];
int slack[maxn];
Info kk[maxn], tt[maxn];
void inin(int N, int M)
{
    int cntm, cnth;
    cntm = cnth = 0;
    memset(w, 0, sizeof(w));
    for(int i = 1; i <= N; i++)
    {
        scanf("%s", s);
        for(int j = 0; j < M; j++)
        {
            if(s[j] == 'm')
            {
                cntm++;
                kk[cntm].x = i;
                kk[cntm].y = j + 1;
            }
            else if(s[j] == 'H')
            {
                cnth++;
                tt[cnth].x = i;
                tt[cnth].y = j + 1;
            }
        }
    }
    n = cntm;
    for(int i = 1; i <= n; i++)
    {
        lx[i] = inf;
        ly[i] = 0;
        for(int j = 1; j <= n; j++)
        {
            int dd = abs(kk[i].x-tt[j].x) + abs(kk[i].y-tt[j].y);
            w[i][j] = dd;
            if(lx[i] > dd)
                lx[i] = dd;
        }
    }
}
bool dfs(int i)
{
    vx[i] = true;
    for(int j = 1; j <= n; j++)
    {
        int xx = w[i][j] - lx[i] - ly[j];
        if(!vy[j] && !xx)
        {
            vy[j] = true;
            if(p[j]==-1 || dfs(p[j]))
            {
                p[j] = i;
                return true;
            }
        }
        else if(slack[j] > xx)
            slack[j] = xx;
    }
    return 0;
}
void solve()
{
    memset(p, -1, sizeof(p));
    for(int i = 1; i <= n; i++)
        while(1)
        {
            memset(vx, false, sizeof(vx));
            memset(vy, false, sizeof(vy));
            memset(slack, 0x3f, sizeof(slack));
            if(dfs(i))
                break;
            int dd = inf;
            for(int i = 1; i <= n; i++)
                if(!vy[i] && dd>slack[i])
                    dd = slack[i];
            for(int i = 1; i <= n; i++)
            {
                if(vx[i])
                    lx[i] += dd;
                if(vy[i])
                    ly[i] -= dd;
            }
        }
    int ans = 0;
    for(int i = 1; i <= n; i++)
        ans += w[p[i]][i];
    printf("%d\n", ans);
}
int main()
{
    int N, M;
    while(scanf("%d%d", &N, &M), N||M)
    {
        inin(N, M);
        solve();
    }
    return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: