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];
} 收集牛客暑期多校训练营的题解
查看4道真题和解析