20516林子鈞
點覆蓋
點獨立集
∣最大匹配∣+∣最小邊覆蓋∣=∣最小點覆蓋∣+∣最大獨立集∣=∣V∣
而且在二分圖上 |最大匹配| = |最小點覆蓋|
(Konig定理)
匹配邊:
對一側的某個點來說他最多找到一次增廣路徑
因為在找到增廣路徑之後他會成為匹配點
匹配點是不能找到增廣路徑的
int n,k,ans=0,vis[2001]={},match[2001]={};//n:存點數量 k:存邊數量 ans:存匹配數 vis[]:存是否被遍歷過 match[]:存匹配的點
vector<int> gra[2001];//儲存圖
int dfs(int pos){
if(vis[pos])return 0;//已遍歷不做事
vis[pos]=1;
for(auto i:gra[pos]){
if(vis[i])continue;//已遍歷不做事
vis[i]=1;
if(match[i]==0||dfs(match[i])){//遞迴式找增廣路徑
match[pos]=i;
match[i]=pos;
return 1;//找到回傳
}
}
return 0;
}
main(){
for(int i=1;i<=n*2;i++){
for(int j=1;j<=n*2;j++)vis[j]=0;
if(match[i]==0)ans+=dfs(i);
}
}
(匈牙利演算法)
匈牙利演算法是個用來求二分圖最大權完美匹配的演算法
因為演算法很大一部分是基於以前匈牙利數學家
Dénes Kőnig和Jenő Egerváry的工作之上建立起來的
故稱作匈牙利演算法(Hungarian algorithm)
Harold Kuhn在1955年提出了此演算法
James Munkres在1977年回顧了此算法
並提出此演算法的複雜度為多項式時間
故也稱Kuhn–Munkres algorithm(KM)
給定一個兩邊各nn個節點的完全二分圖
(也可以以矩陣方式給)
每條邊(u,v)都有權重c(u,v)c(u, v)
找到一個權重最大的完美匹配
給每個點一個點權\(l(u)\)
使得每個邊\((u,v)\)符合\(l(u)+l(v)\geq c(u,v)\)
當一個邊\((u,v)\)符合\(l(u)+l(v)= c(u,v)\)時
稱之為緊邊
對一個二分圖中的緊邊集合稱之為緊邊子圖
當緊邊子圖中找到完美匹配時
所求的最大權重即為\(\Sigma l(u)\)
緊邊
匹配邊
非緊邊
緊邊
匹配邊
非緊邊
\(+\Delta\)
\(+\Delta\)
\(-\Delta\)
\(-\Delta\)
\(\Delta\)為藍邊中\(l(u)+l(v)-c(u,v)\)最小的
int vx[MAXN]={},vy[MAXN]={},mx[MAXN]={},my[MAXN]={},lx[MAXN]={},ly[MAXN]={};
//vx:左側記錄是否遍歷過 vy:右側紀錄是否遍歷過 mx:左側紀錄匹配點 my:右側記錄匹配點 lx:左側點權 ly:右側點權
int w[MAXN][MAXN],n;
int dfs(int pos){
if(vx[pos])return 0;
vx[pos]=1;
for(int i=1;i<=n;i++){
if(lx[pos]+ly[i]>w[pos][i])continue;//非緊邊不跑
vy[i]=1;
if(my[i]==0||dfs(my[i])){
mx[pos]=i;//交錯路徑改匹配
my[i]=pos;
return 1;
}
}
return 0;
}
int main(){
cin>>n;
while(1){
if(n<=0)break;
for(int i=1;i<=n;i++)mx[i]=0,my[i]=0,lx[i]=0,ly[i]=0;
for(int i=1;i<=n;i++)for(int j=1;j<=n;j++){
cin>>w[i][j];//輸入邊權
if(w[i][j]<0)w[i][j]=0;
}
vx[0]=1;
vy[0]=1;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
lx[i]=max(lx[i],w[i][j]);//設定左側點權
}
}
for(int i=1;i<=n;i++){//遍歷點
while(1){//找不到增廣路徑就一直跑
for(int i=1;i<=n;i++)vx[i]=0,vy[i]=0;
if(dfs(i)){//找到增廣路徑就跳出
break;
}
int del=(1<<30);
for(int j=1;j<=n;j++){
if(!vx[j])continue;
for(int k=1;k<=n;k++){
if(!vy[k]){
del=min(del,lx[j]+ly[k]-w[j][k]);//設定delta
}
}
}
for(int j=1;j<=n;j++){
if(vx[j])lx[j]-=del;//改delta
if(vy[j])ly[j]+=del;
}
}
}
int ans=0;
for(int i=1;i<=n;i++){
ans+=lx[i]+ly[i];//算答案
}
cout<<ans<<endl;
cin>>n;
}
}
有一些員工要完成一些任務。 各個員工完成不同任務所花費的時間都不同。每個員工只分配一項任務。 每項任務只被分配給一個員工。 怎樣分配員工與任務以使所花費的時間最少?