1. 程式人生 > >[p1559] 運動員最佳匹配問題

[p1559] 運動員最佳匹配問題

ems ron href while pty HR In 二分 ffffff

題目

根據題目,我們很快能看出來,這是一個帶權的二分圖匹配問題。

二分圖匹配,我們可以跑最大流

而帶上權值呢? 就可以跑最小費用最大流233。

啥? 不是求最大嗎? 怎麽可以跑最小費用?

其實是可以的。 對於最基礎的最小費用最大流:每次求一條可以增廣的最短路。然後增廣。

而我們根據這個題,我們需要求得是最大,也就是最長路。

我們就可以,將權值取負數,然後用SPFA跑最短路。

這樣問題就迎刃而解了。(dij好像也可以,只不過我太vegetable,不會

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue> #include<cstring> using namespace std; struct node { int point; int flow; int val; int nxt; }; node line[100000];//前項星 int head[100],tail=-1; void add(int x,int y,int f,int v) { line[++tail].point=y; line[tail].flow=f; line[tail].val=v; line[tail].nxt=head[x]; head[x]=tail; }//加邊
int male[21][21]; int famale[21][21];//讀入的數據,看數組名就可以了,不需要再解釋了233 int max_cost;//最大的費用,就是答案 int flow[100],last[100],dis[100];//last是把每個點更新的邊的編號 int pre[100]; bool inque[100];//SPFA找最短的增廣路所需要的數組 bool SPFA(int begin,int end) { queue<int>q; memset(dis,10,sizeof(dis)); memset(flow,0,sizeof(flow)); memset(inque,0
,sizeof(inque)); memset(pre,0,sizeof(pre));//初始化 dis[begin]=0;pre[begin]=0;flow[begin]=0x7fffffff;//源點流量無限 inque[begin]=true;q.push(begin); while(!q.empty()) { int pas=q.front(); q.pop(); inque[pas]=false; for(int i=head[pas];i!=-1;i=line[i].nxt)//遍歷 if(line[i].flow>0&&dis[line[i].point]>dis[pas]+line[i].val)//一定要判斷是否可以增廣 { dis[line[i].point]=dis[pas]+line[i].val;//最短路 last[line[i].point]=i;pre[line[i].point]=pas;//儲存邊的編號,以便後來更改 flow[line[i].point]=min(flow[pas],line[i].flow);//流量更新,其實是不需要的,因為我們現在在做二分圖匹配嘛 if(!inque[line[i].point]) { inque[line[i].point]=true; q.push(line[i].point); } } } if(!pre[end]) return false;//匯點是否到達 return true; } void EK(int begin,int end) { while(SPFA(begin,end)) { int pas=end; max_cost+=dis[end]*flow[end]*-1;//因為我們取負存嘛,所以要倒回來 while(pas!=begin)//從匯點到源點進行增廣 { line[last[pas]].flow-=flow[end]; line[last[pas]^1].flow+=flow[end];//last就用上了 pas=pre[pas]; } } return ; } int main() { int n; scanf("%d",&n); int len=n; for(int i=0;i<=(len<<1)+1;i++) head[i]=-1; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) scanf("%d",&male[i][j]); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) scanf("%d",&famale[i][j]);//初始化與輸入 for(int i=1;i<=n;i++)//建圖,0為源點,n*2+1為匯點 { add(0,i,1,0),add(i,0,0,0); add(len+i,(len<<1)+1,1,0),add((len<<1)+1,len+i,0,0); } for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) add(i,len+j,1,male[i][j]*famale[j][i]*-1),add(len+j,i,0,male[i][j]*famale[j][i]);//記住取負 EK(0,(len<<1)+1); printf("%d",max_cost); }

[p1559] 運動員最佳匹配問題