訓練指南 UVA - 11383(KM算法的應用 lx+ly >=w(x,y))
阿新 • • 發佈:2019-02-04
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))