UVA 11383 Golden Tiger Claw(KM算法)

题目:http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=2378

题目大意:给你一个 n*n 的矩阵,要求你对每行和每列设一个值 row(i)和col(i),设的对于任意格子w( i , j ) ,满足 w( i , j ) <= row( i ) + col( j ) ,并且所有的 row( i ) 和 col( i )的和最小。

解题思路:这个在二分图匹配中粗现,但是实在看不出有什么可以和二分图联系上的,后来看了书才发现,原来这道题是 KM 算法的一个副产品。在 KM 里顶标 Lx 和 Ly,Lx( x ) + Ly( y ) >= w( x , y ) ,然后就根据这个不等式,直接上 KM 就行。算法结束后,所有的顶标和是最小的。 (这句话需要好好理解!)

        很好的一道开拓性思维的题目啊!~~~~

代码如下:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int INF = 0x0fffffff;
const int MAXN = 555;

int n;

int w[MAXN][MAXN];
int lx[MAXN],ly[MAXN],slack[MAXN];
int s[MAXN],t[MAXN],left[MAXN];

int match(int i)
{
    s[i] = 1;
    for(int j = 1;j <= n;j++)
        if(!t[j])
        {
            int tmp = lx[i]+ly[j]-w[i][j];
            if(tmp == 0)
            {
                t[j] = 1;
                if(!left[j] || match(left[j]))
                {
                    left[j] = i;
                    return 1;
                }
            }
            else slack[j] = min(slack[j],tmp);
        }
    return 0;
}

void update()
{
    int a = INF;
    for(int i = 1;i <= n;i++)
        if(!t[i]) a = min(a,slack[i]);
    for(int i = 1;i <= n;i++)
    {
        if(s[i]) lx[i] -= a;
        if(t[i]) ly[i] += a;
    }
}

void km()
{
    for(int i = 1;i <= n;i++)
    {
        left[i] = lx[i] = ly[i] = 0;
        for(int j = 1;j <= n;j++)
            lx[i] = max(lx[i],w[i][j]);
    }
    for(int i = 1;i <= n;i++)
    {
        for(int j = 1;j <= n;j++)
            slack[j] = INF;
        while(1)
        {
            for(int j = 1;j <= n;j++) s[j] = t[j] = 0;
            if(match(i))
                break;
            else update();
        }
    }
}

int main()
{
    while(~scanf("%d",&n))
    {
        for(int i = 1;i <= n;i++)
            for(int j = 1;j <= n;j++)
                scanf("%d",&w[i][j]);
        km();
        int sum = lx[1];
        printf("%d",lx[1]);
        for(int i = 2;i <= n;i++)
        {
            sum += lx[i];
            printf(" %d",lx[i]);
        }
        puts("");
        sum += ly[1];
        printf("%d",ly[1]);
        for(int i = 2;i <= n;i++)
        {
            sum += ly[i];
            printf(" %d",ly[i]);
        }
        puts("");
        printf("%d\n",sum);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值