2020牛客暑期多校训练营(第一场)H
Minimum-cost Flow
https://ac.nowcoder.com/acm/contest/5666/H
题目描述
Bobo has a network of nodes and arcs. The -th arc goes from the -th node to the -th node, with cost .
Bobo also asks questions. The -th question is specified by two integers and , which is to ask the minimum cost to send one unit of flow from the -st node to the -th node, when all the edges have capacity (a fraction).
You can refer the wiki page for further information of Minimum-cost Flow.
输入描述
The input consists of several test cases terminated by end-of-file.
The first line of each test case contains two integers and . The -th of the following lines contains three integers and . The next line contains an integer . The -th of the last lines contains two integers and .
, , , .
, , .
The sum of does not exceed . The sum of does not exceed .
输出描述
For each test case, print fractions (or NaN
, if it is impossible to send one unit of flow) which denote the answer.
示例1
输入
2 1
1 2 2
1
1 1
2 2
1 2 1
1 2 2
3
1 2
2 3
1 4
输出
2/1
3/2
4/3
NaN
分析
运行 次最小费用流算法显然是不现实的,应当考虑的是:运行一次最小费用流,进行一些预处理,使得每次询问时能够在较短时间内给出答案。
题中有一重要信息:每条边的容量都相同。设这个容量为 。利用 算法求最小费用最大流,会不断用 算法寻找单位费用和最小的增广路;由于每条边的容量都为 ,故每条边只会存在满流或零流的情况,每次增广获得的最大流改进量恒为 。若源点流量为 且 不超过最大流,就是最小费用流模型。对于这样特殊的(每条边容量都为 )最小费用流模型,仍然可以利用 算法,每次增广后相当于将流量 减少 ,产生的费用是增广路单位费用和与 的乘积;而最后一次增广时费用的改进量为剩余流量 与增广路单位费用和的乘积。进行多轮增广,直至剩余流量为零,就能得到最小费用流。
由于每条边的费用 是不变的,因此不论 如何变化,只要源点流量不超过当前网络的最大流,对 次询问中的每一张网络进行 算法,找到的增广路都是相同的,且每条增广路的顺序也保持不变。因此,只需要进行一次最小费用流的计算,记录下每一条增广路的单位费用即可,根据 算法原理,这些增广路是按照单位费用和由小到大获得的,排序操作是多余的。
不妨令每条边的容量为 ,求一次最小费用最大流,记录下每一条增广路的单位费用。对于每一次询问,源点流量为 ,每条边容量为 。为了减少计算的误差,可以将所有数量扩大 倍,即网络中源点流量为 ,每条边容量为 。若源点流量 不超过网络最大流,那么求固定流量 的最小费用流得过程中,增广路为最小费用最大流得增广路子集,且每一轮 获得的增广路一定是当前所有存在的增广路中费用最小的。每一轮增广,源点流量就减少 ,相应的费用为该增广路的单位费用与 的乘积;最后一次增广,源点流量减少 ,对应费用也要相应改变;最终,源点流量为 ,求得最小费用流 。 即为一次询问的答案。
代码
/****************************************************************** Copyright: 11D_Beyonder All Rights Reserved Author: 11D_Beyonder Problem ID: 2020牛客暑期多校训练营(第一场) Problem H Date: 7/24/2020 Description: Minimum-cost Flow *******************************************************************/ #include<cstring> #include<vector> #include<algorithm> #include<cstdio> #include<queue> using namespace std; typedef long long ll; const int N=104; const int M=203; const ll inf=0x3f3f3f3f3f3f3f3f; int n,m;//n个点,m条边 int tot; int head[N]; struct E { int to; int Next; ll cap;//容量 ll cost;//费用 }edge[M<<2]; vector<ll>span;//增广路单位费用 int pre[N];//记录增广路前驱 ll incf[N];//增广路上剩余最小容量 ll dis[N];//将费用看作边权求最短路 bool vis[N];//记录访问 int s,t;//源点 汇点 ll maxflow,mincost;//最小费用最大流 inline void init();//初始化 inline void add_edge(int,int,int,int); bool SPFA();//找增广路 void update();//更新最长增广路容量 int main() { while(~scanf("%d%d",&n,&m)) { init(); while(m--) { int u,v,c; scanf("%d%d%d",&u,&v,&c); add_edge(u,v,1,c); add_edge(v,u,0,-c); } while(SPFA()) { update(); //记录每一条增广路的单位费用 span.push_back(dis[t]); } int q; scanf("%d",&q); while(q--) { ll u,v; scanf("%lld%lld",&u,&v); //流量v 容量u if(maxflow*u<v) puts("NaN");//流量大于最大流 else { ll flow=v,cap=u; ll ans=0; //按照费用由小到大的顺序访问所有增广路 for(auto cost:span) { if(flow>=u) { flow-=u; ans+=u*cost; } else { ans+=flow*cost; break; } } //输出最简分式 ll g=__gcd(ans,v); printf("%lld/%lld\n",ans/g,v/g); } } } return 0; } inline void init() { s=1; t=n; memset(head,-1,sizeof(head)); tot=1; span.clear(); memset(pre,0,sizeof(pre)); maxflow=mincost=0; } inline void add_edge(int u,int v,int cap,int cost) { tot++; edge[tot].to=v; edge[tot].cap=cap; edge[tot].cost=cost; edge[tot].Next=head[u]; head[u]=tot; } bool SPFA() { queue<int>q; memset(dis,inf,sizeof(dis)); memset(vis,0,sizeof(vis)); q.push(s); dis[s]=0; vis[s]=1; incf[s]=inf; while(!q.empty()) { int x=q.front(); vis[x]=0; q.pop(); for(register int i=head[x];~i;i=edge[i].Next) { if(!edge[i].cap) continue;//剩余容量为0,不在残量网络中 int y=edge[i].to; if(dis[y]>dis[x]+edge[i].cost) { dis[y]=dis[x]+edge[i].cost;//松弛操作 incf[y]=min(incf[x],edge[i].cap);//最小剩余容量 pre[y]=i;//记录前驱 if(!vis[y]) { vis[y]=1; q.push(y); } } } } if(dis[t]==inf) return 0;//汇点不可达,已经求出最大流 else return 1; } void update() { int x=t; //沿着前驱倒着走增广路 while(x!=s) { int y=pre[x]; edge[y].cap-=incf[t]; edge[y^1].cap+=incf[t]; x=edge[y^1].to; } maxflow+=incf[t]; mincost+=dis[t]*incf[t]; }
收集牛客暑期多校训练营的题解