题目链接:这里
本题是一个典型的费用流问题,可以作为费用流建图模板使用
首先看到,每个人只能做一件工作,每件工作只能做一次,一个人做某件工作有一定的收益
那么我们建立一个超级源点st和超级终点ed,然后由源点向所有人连边,容量为1,费用为0
接着由工作向汇点连边,容量为1费用为0
上面满足了每个人只做一件工作且每件工作只做一次的要求
最后由人向工作连边,容量为1费用为收益,跑一遍费用流即为最小收益
然后把人向工作连边的边权取负值,再跑一遍费用流,此时最小费用的相反数即为最大收益
这道题向我们提示:最大费用流也是可以跑的,只是将所有费用取反后跑最小费用流即可
#include <cstdio> #include <cmath> #include <cstring> #include <cstdlib> #include <iostream> #include <algorithm> #include <queue> #include <stack> using namespace std; const int INF=0x3f3f3f3f; struct Edge { int next; int to; int val; int pri; }edge[20005]; int head[255]; int dis[255]; int pre[255]; int fa[255]; int lim[255]; bool used[255]; int a[255][255]; int cnt=1; int n; int st,ed; void init() { memset(head,-1,sizeof(head)); memset(edge,0,sizeof(edge)); cnt=1; } void add(int l,int r,int w,int v) { edge[cnt].next=head[l]; edge[cnt].to=r; edge[cnt].val=w; edge[cnt].pri=v; head[l]=cnt++; } int ide(int x) { return (x&1)?x+1:x-1; } bool spfa() { memset(dis,0x3f,sizeof(dis)); memset(lim,0,sizeof(lim)); memset(used,0,sizeof(used)); dis[st]=0; lim[st]=INF; pre[ed]=-1; used[st]=1; queue <int> M; M.push(st); while(!M.empty()) { int u=M.front(); M.pop(); for(int i=head[u];i!=-1;i=edge[i].next) { int to=edge[i].to; if(edge[i].val&&dis[to]>dis[u]+edge[i].pri) { dis[to]=dis[u]+edge[i].pri; lim[to]=min(lim[u],edge[i].val); pre[to]=i; fa[to]=u; if(!used[to])used[to]=1,M.push(to); } } used[u]=0; } return pre[ed]!=-1; } int EK() { int maxw=0,minv=0; while(spfa()) { minv+=dis[ed]*lim[ed]; maxw+=lim[ed]; int temp=ed; while(temp!=st) { edge[pre[temp]].val-=lim[ed]; edge[ide(pre[temp])].val+=lim[ed]; temp=fa[temp]; } } return minv; } int main() { scanf("%d",&n); init(); for(int i=2;i<=n+1;i++) { for(int j=n+2;j<=2*n+1;j++) { int x; scanf("%d",&x); add(i,j,1,x); add(j,i,0,-x); a[i-1][j-n-1]=x; } } st=1,ed=2*n+2; for(int i=2;i<=n+1;i++)add(st,i,1,0),add(i,st,0,0); for(int i=n+2;i<=2*n+1;i++)add(i,ed,1,0),add(ed,i,0,0); printf("%d ",EK()); init(); for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) { add(i+1,j+n+1,1,-a[i][j]); add(j+n+1,i+1,0,a[i][j]); } } st=1,ed=2*n+2; for(int i=2;i<=n+1;i++)add(st,i,1,0),add(i,st,0,0); for(int i=n+2;i<=2*n+1;i++)add(i,ed,1,0),add(ed,i,0,0); printf("%d ",-EK()); return 0; }