2020牛客暑期多校训练营(第一场)H-Minimum-cost Flow(最小费用流)
题目链接
题目大意:初始给定一个\(n\)个点,\(m\)条边,费用全部确定的网络。要求对于接下来的\(k\)次询问,每次都给定所有边的容量为一个分数\(\frac{u}{v}\)。要求对于每一个询问计算从点\(1\)到点\(n\)跑大小为\(1\)的流的最小花费。
大致思路:因为我们事先知道了所有边的费用,并且后续所有边的最大容量全部相同。那么初始建图的时候所有边的最大容量都设置为\(1\),这样我们只需要跑一边最小费用最大流,并且记录每一次的增广路径的花费(由于容量为\(1\),所以最少花费就是最短路的距离)。然后对于每一次询问我们进行分析:由于容量给定的是一个分数,所以我们先将所有边的容量扩大至\(v\)倍,那么所有边的最大容量就变成了\(u\)。所以现在我们要大小为\(v\)的流,最后得出的花费除以\(v\)就是最终所求的答案。由于所有的最大容量都为\(u\),所以每一次的增广所增加的流量都为\(u\)。最后一次把剩余的流量补全即可。如果最大流无法达到\(v\),就输出\(NaN\)。具体看代码实现。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
class MinCostFlow {
public:
struct Result {
int flow, cost;
};
MinCostFlow(int n, int m = 0) : visited(n), head(n, -1), dist(n), prev(n) ,flow(n), preu(n){
edges.reserve(m << 1);
}
void add_edge(int u, int v, int capacity, int cost) {
internal_add_edge(u, v, capacity, cost);
internal_add_edge(v, u, 0, -cost);
}
bool spfa(int src, int dst) {
const int infdist = std::numeric_limits<int>::max();
std::fill(dist.begin(), dist.end(), infdist);
std::fill(flow.begin(), flow.end(), infdist);
dist[src] = 0;
std::queue<int> queue;
queue.push(src);
while (!queue.empty()) {
int u = queue.front();
queue.pop();
visited[u] = false;
for (int iter = head[u]; ~iter;) {
int v = edges[iter].v;
if (edges[iter].rest && dist[u] + edges[iter].cost < dist[v]) {
dist[v] = dist[u] + edges[iter].cost;//费用增广路的最短路
prev[v] = iter;//上一边
preu[v] = u;//前驱点
flow[v] = min(flow[u],edges[iter].rest);//增广的瓶颈流量
if (!visited[v]) {
visited[v] = true;
queue.push(v);
}
}
iter = edges[iter].next;
}
}
return dist[dst]!=infdist;
}
vector<int> ans;
Result MCMF(int src,int dst){
int maxflow=0,mincost=0;
while(spfa(src,dst)){
int v=dst;
ans.push_back(dist[dst]);
maxflow+=flow[dst];
mincost+=flow[dst]*dist[dst];
while(v!=src){
edges[prev[v]].rest-=flow[dst];
edges[prev[v]^1].rest+=flow[dst];
v=preu[v];
}
}
return Result{maxflow,mincost};
}
private:
struct Edge {
int v, next, rest, cost;//终点,下条边,容量,花费
};
void internal_add_edge(int u, int v, int capacity, int cost) {
edges.push_back(Edge{v, head[u], capacity, cost});
head[u] = edges.size() - 1;
}
std::vector<bool> visited;
std::vector<int> head, dist, prev, flow, preu;
std::vector<Edge> edges;
};
int main(){
int n,m;
while(cin>>n>>m){
MinCostFlow net(n,m);
for(int i=0,a,b,c;i<m;i++){
cin>>a>>b>>c;
net.add_edge(a-1,b-1,1,c);
}
auto res=net.MCMF(0,n-1);
int q;
cin>>q;
while(q--){
int u,v;
scanf("%d%d",&u,&v);
if(u==0)puts("NaN");
else{
ll fz=0,fm=v;
for(int an : net.ans){
if(v>u)fz+=1ll*an*u,v-=u;
else {fz+=1ll*an*v,v=0;break;}
}
if(v)puts("NaN");
else{
ll gd=__gcd(fz,fm);
printf("%lld/%lld\n",fz/gd,fm/gd);
}
}
}
}
return 0;
}