1. 程式人生 > >訓練指南 UVA - 11383(KM算法的應用 lx+ly >=w(x,y))

訓練指南 UVA - 11383(KM算法的應用 lx+ly >=w(x,y))

amp 個數字 mat ret out long pro 格子 匹配


layout: post
title: 訓練指南 UVA - 11383(KM算法的應用 lx+ly >=w(x,y))
author: "luowentaoaa"
catalog: true
mathjax: true
tags:
- KM算法
- 訓練指南


Golden Tiger Claw

UVA - 11383

題意

給一個n*n的矩陣,每個格子中有正整數w[i[j],試為每行和每列分別確定一個數字row[i]和col[i],使得任意格子w[i][j]<=row[i]+col[j]恒成立。先輸row,再輸出col,再輸出全部總和(總和應盡量小)。

思路

本題與匹配無關,但可以用KM算法解決。

  KM算法中的頂標就是保持了Lx[i]+ly[j]>=g[i[j]再求最大權和匹配的,但這個最大權和並沒有關系。我們可以將row[i]看成一個男的,col[i]看成一個女的,這樣男女的總數就相等。

  一般來說,Lx[i]或Ly[i]僅需要取該行/列中最大的那個數即可保證滿足要求,但是這樣太大了,可以通過調整來使得總和更小。而KM算法的過程就是一個調整的過程,每一對匹配的男女的那條邊的權值就會滿足等號 wi[j]=row[i]+col[j],至少需要一個來滿足等號,這樣才能保證row[i]+col[j]是達到最小的,即從j列看,col[j]滿足條件且最小,從i行看,row[i]滿足條件且最小。這剛好與KM算法求最大權和一樣。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=998244353;
const int maxn=5e2+50;
const ll inf=1e10;
const ll INF = 1000000000;
const double eps=1e-5;
int g[530][530];  ///存圖
int nx,ny; /// 兩邊點數
bool visx[maxn],visy[maxn];
int slack[maxn];
int linker[maxn];   ///y中各點匹配狀態
int lx[maxn],ly[maxn]; /// x,y中的點標號
bool dfs(int x){
    visx[x]=true;
    for(int y=0;y<ny;y++){
        if(visy[y])continue;
        int tmp=lx[x]+ly[y]-g[x][y];
        if(tmp==0){
            visy[y]=true;
            if(linker[y]==-1||dfs(linker[y])){
                linker[y]=x;return true;
            }
        }
        else if(slack[y]>tmp)slack[y]=tmp;
    }
    return false;
}
int KM(){
    memset(linker,-1,sizeof(linker));
    memset(ly,0,sizeof(ly));
    for(int i=0;i<nx;i++){
        lx[i]=-inf;
        for(int j=0;j<ny;j++){
            if(g[i][j]>lx[i])lx[i]=g[i][j];
        }
    }
    for(int x=0;x<nx;x++){
        for(int i=0;i<ny;i++)slack[i]=inf;
        while(true){
            memset(visx,false,sizeof(visx));
            memset(visy,false,sizeof(visy));
            if(dfs(x))break;
            int d=inf;
            for(int i=0;i<ny;i++)
                if(!visy[i]&&d>slack[i])d=slack[i];
            for(int i=0;i<nx;i++)
                if(visx[i])lx[i]-=d;
            for(int i=0;i<ny;i++)
                if(visy[i])ly[i]+=d;
                else slack[i]-=d;
        }
    }
    int res=0;
    for(int i=0;i<ny;i++)
        if(linker[i]!=-1)res+=g[linker[i]][i];
    return res;
}

int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    std::cout.tie(0);
    int n;
    while(cin>>n){
        nx=ny=n;
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)cin>>g[i][j];
        int ans=KM();
        cout<<lx[0];
        for(int i=1;i<n;i++)cout<<" "<<lx[i];cout<<endl;
        cout<<ly[0];
        for(int i=1;i<n;i++)cout<<" "<<ly[i];cout<<endl;
        cout<<ans<<endl;
    }

    return 0;
}

訓練指南 UVA - 11383(KM算法的應用 lx+ly >=w(x,y))