XJOI夏令营501训练1——分配工作
传送门:QAQQAQ
题意:某公司有工作人员x1,x2,…,xn ,他们去做工作y1,y2,…,ym(n<=m) ,每个人都能做其中的几项工作,并且对每一项工作都有一个固定的效率。问能否找到一种合适的工作分配方案,使得总的效率最高。要求一个人只能参与一项工作,同时一项工作也必须由一个人独立完成。不要求所有的人都有工作。
思路:首先让我们明确二分图匹配算法和网络流的区别:二分图KM算法是网络流最小费用最大流中的特例,即KM算法必须满足每一个x点都被匹配才能运行,但两个算法的前提都是在最大匹配的情况下最优,而这道题显然没有要求要最大匹配,所以这两种算法均被否定……(本来想过用最大流做,但发现最大流无法限制x节点只能连一条边)
所以我们要改进KM算法,我们现在要做的是令所有连边都满足最大匹配的前提,这样效率高低就会成为判断方案优劣的唯一因素。(这种控制无关因素的思路值得借鉴)
我们现在把每个人干不了的工作都连上0的边,表示这个人干他干他干不了的工作效率为0,这样每个人和每个工作就都有了边,因为m>=n,所以每个人都可以搜到,再跑一遍KM求答案即可。
(其实这种思路来改进最小费用最大流也可以,但很麻烦,因为网络流中残量网络为0时跑不了)
上代码:
#include<bits/stdc++.h> using namespace std; const int N=505; const int M=1000001; const int inf=(int)2e9; int E[505][505]; int match[505],lx[505],ly[505],visx[505],visy[505],slack[505],n,m; bool dfs(int u) { visx[u]=1; for(int v=1;v<=m;v++) { if(visy[v]) continue; if(lx[u]+ly[v]==E[u][v]) { visy[v]=1; if(match[v]==-1||dfs(match[v])) { match[v]=u; return 1; } } else { slack[v]=min(slack[v],lx[u]+ly[v]-E[u][v]); } } return 0; } void EK() { memset(visx,0,sizeof(visx)); memset(visy,0,sizeof(visy)); for(int x=1;x<=n;x++) { for(int i=1;i<=m;i++) slack[i]=inf; while(true) { memset(visx,0,sizeof(visx)); memset(visy,0,sizeof(visy)); if(dfs(x)) break; int Delta=inf; for(int i=1;i<=m;i++) if(!visy[i]) Delta=min(Delta,slack[i]); for(int i=1;i<=n;i++) if(visx[i]) lx[i]-=Delta; for(int i=1;i<=m;i++) if(visy[i]) ly[i]+=Delta; else slack[i]-=Delta; } } } int main() { memset(E,0,sizeof(E)); memset(ly,0,sizeof(ly)); memset(match,-1,sizeof(match)); int k; scanf("%d%d%d",&n,&m,&k); for(int i=1;i<=k;i++) { int x,y,z; scanf("%d%d%d",&x,&y,&z); E[x][y]=max(E[x][y],z); } for(int i=1;i<=n;i++) { lx[i]=0; for(int j=1;j<=m;j++) lx[i]=max(lx[i],E[i][j]); } EK(); int ans=0; for(int i=1;i<=m;i++) { if(match[i]!=-1) ans+=E[match[i]][i]; } cout<<ans<<endl; return 0; }