码迷,mamicode.com
首页 > 其他好文 > 详细

P - 奔小康赚大钱 - hdu 2255(带权值的匹配)

时间:2015-08-06 08:15:17      阅读:102      评论:0      收藏:0      [点我收藏+]

标签:

分析:这是一个KM的模板题,也就不多说了,KM最复杂的情况都能过,下面是没有优化过的代码:
************************************************************
技术分享
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;

const int MAXN = 305;
const int oo = 1e9+7;

int w[MAXN][MAXN], N;
int dx[MAXN], dy[MAXN];
int Ly[MAXN], slack[MAXN];
bool vx[MAXN], vy[MAXN];

bool Find(int i)
{///别忘记vx[i]赋值true,表示可以增广的时候达到了i点
    vx[i] = true;
    for(int j=1; j<=N; j++)
    {
        if( !vy[j] && dx[i]+dy[j] == w[i][j] )
        {
            vy[j] = true;

            if( !Ly[j] || Find(Ly[j]) )
            {
                Ly[j] = i;
                return true;
            }
        }
        ///else if(vy[j] == false)
           
/// slack[j] = min(slack[j], dx[i]+dy[j]-w[i][j]);
    }

    return false;
}
int KM()
{
    int i, j, k;

    for(i=1; i<=N; i++) while(1)
    {///给每个点进行增广,找不到减去一个d,继续找

        memset(vx, falsesizeof(vx));
        memset(vy, falsesizeof(vy));

        if( Find(i) == truebreak;

        int d = oo;

        for(j=1; j<=N; j++) if( vx[j] )
        for(k=1; k<=N; k++) if( !vy[k] )
        {///用访问过的左边和未访问过的右边求出来一个最小的d
            d = min( d,  dx[j]+dy[k]-w[j][k]);
        }

        for(j=1; j<=N; j++)
        {///左边的标杆减去d,右边的加上d,这样原来匹配的值没有改变
         
///减去d的目的是为了查找那个可以增广的边里面最大的那个
            if(vx[j])dx[j] -= d;
            if(vy[j])dy[j] += d;
        }
    }

    int sum = 0;

    for(i=1; i<=N; i++)
    {
        sum += w[ Ly[i] ][i];
    }

    return sum;
}

int main()
{
    while(scanf("%d", &N) != EOF)
    {
        memset(dx, falsesizeof(dx));
        memset(dy, falsesizeof(dy));
        memset(Ly, falsesizeof(Ly));

        for(int i=1; i<=N; i++)
        for(int j=1; j<=N; j++)
        {
            scanf("%d", &w[i][j]);
            dx[i] = max(dx[i], w[i][j]);
        }

        printf("%d\n", KM());
    }

    return 0;
}
View Code

 

 

*********************************************************************
下面是优化过的,感觉时间上减少的不是那么明显,少了100多ms
技术分享
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;

const int MAXN = 305;
const int oo = 1e9+7;

int w[MAXN][MAXN], N;
int dx[MAXN], dy[MAXN];
int Ly[MAXN], slack[MAXN];
bool vx[MAXN], vy[MAXN];

bool Find(int i)
{///别忘记vx[i]赋值true,表示可以增广的时候达到了i点
    vx[i] = true;
    for(int j=1; j<=N; j++)
    {
        if( !vy[j] && dx[i]+dy[j] == w[i][j] )
        {
            vy[j] = true;

            if( !Ly[j] || Find(Ly[j]) )
            {
                Ly[j] = i;
                return true;
            }
        }
        else if(vy[j] == false)
            slack[j] = min(slack[j], dx[i]+dy[j]-w[i][j]);
    }

    return false;
}
int KM()
{
    int i, j;

    for(i=1; i<=N; i++)
    {
        for(j=1; j<=N; j++)
            slack[j] = oo;

        while(true)
        {
            memset(vx, falsesizeof(vx));
            memset(vy, falsesizeof(vy));

            if( Find(i) == true )break;

            int d = oo;

            for(j=1; j<=N; j++)
            {
                if(!vy[j] && d > slack[j])
                    d = slack[j];
            }

            for(j=1; j<=N; j++)
            {
                if(vx[j])dx[j] -= d;
                if(vy[j])dy[j] += d;
                else slack[j] -= d;
            }
        }
    }

    int sum = 0;

    for(i=1; i<=N; i++)
    {
        sum += w[ Ly[i] ][i];
    }

    return sum;
}

int main()
{
    while(scanf("%d", &N) != EOF)
    {
        memset(dx, falsesizeof(dx));
        memset(dy, falsesizeof(dy));
        memset(Ly, falsesizeof(Ly));

        for(int i=1; i<=N; i++)
        for(int j=1; j<=N; j++)
        {
            scanf("%d", &w[i][j]);
            dx[i] = max(dx[i], w[i][j]);
        }

        printf("%d\n", KM());
    }

    return 0;
}
View Code

 

P - 奔小康赚大钱 - hdu 2255(带权值的匹配)

标签:

原文地址:http://www.cnblogs.com/liuxin13/p/4706346.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!