zoukankan      html  css  js  c++  java
  • 2020牛客暑期多校训练营(第一场)H Minimum-cost Flow

    Minimum-cost Flow

    题目:给n个点,m条边。接下来m行包含(a,b,c),即a,b之间有单位流量代价为c的边。接下来有q个问题,每个问题给定(x,y),即假设每条边的容量为x/y时,从点1到点n流1单位的流量,最少的花费是多少,如果无法从点1到点n流1单位的流量则输出“NaN”。

    思路:首先我们需要想到一个结论,每条边最多只能使用一次,这个自己比划一下就可以,其中包含贪心的想法。得到这个结论后,我们发现整张图就变成了流量为1的网络流。然后,根据(x,y)说明我们至少需要 y / x + (y % x != 0)条从1到n的最少花费路径,这个我们可以通过最小费用流预处理得到所有能够得到的最小花费路径,并记录每条路径的最小花费。这样,对于每个询问(x,y),我们只需要取前y / x小的边,如果y % x != 0,则从后面的边补充一些即可。

    补充:为什么我们可以直接这样直接取路径,看上图,我们得到的最小花费应该是两个:3,21。这是懂得网络流算法都能看出的,如果我们有组询问(2,3),我们可以得到答案应该是ans = 3 * 2/3 + 21 * 1/3 = 9。但这两条路分别是1->2->3->4和1->3->2->4得到的,会感觉有点奇怪“这样就是答案”。其实我们需要回到网络流的反向边在算法中的用途"反流"和正向边可以把"反流"失去的流量再"补流",下面附一个来体现ans = 9是怎么得到的,然后思考。

      1 #include <iostream>
      2 #include <cstdio>
      3 #include <algorithm>
      4 #include <queue>
      5 #include <vector>
      6 #include <cstring>
      7 
      8 using namespace std;
      9 
     10 #define ll long long
     11 #define pb push_back
     12 #define fi first
     13 #define se second
     14 
     15 const int N = 100;
     16 const int M = 200;
     17 const int INF = 1e9 + 7e8;
     18 struct edge
     19 {
     20     int to, nxt, cap, flow, w;
     21 }e[M << 1];
     22 int head[N], d[N], vis[N], pre[N];
     23 queue<int > que;
     24 int  a[N], b[N];
     25 int n, m, tot, s, t, cnt;
     26 
     27 inline void add(int u, int v, int w)
     28 {
     29     e[tot].to = v; e[tot].w = w; e[tot].cap = 1;
     30     e[tot].flow = 0; e[tot].nxt = head[u]; head[u] = tot++;
     31     e[tot].to = u; e[tot].w = -w; e[tot].cap = 0;
     32     e[tot].flow = 0; e[tot].nxt = head[v]; head[v] = tot++;
     33 }
     34 
     35 bool spfa()
     36 {
     37     for(int i = 1; i <= n; ++i) d[i] = INF, vis[i] = false, pre[i] = -1;
     38     while(!que.empty()) que.pop();
     39     d[s] = 0; vis[s] = true; pre[s] = -1;
     40     que.push(s);
     41     //printf("s = %d  t = %d
    ", s, t);
     42     while(!que.empty()){
     43         int now = que.front();
     44         que.pop();
     45         vis[now] = false;
     46     
     47         for(int o = head[now]; ~o; o = e[o].nxt){
     48             if(e[o].cap - e[o].flow && d[e[o].to] > d[now] + e[o].w){
     49                 d[e[o].to] = d[now] + e[o].w;
     50                 pre[e[o].to] = o;
     51                 if(!vis[e[o].to]){
     52                     vis[e[o].to] = true;
     53                     que.push(e[o].to);
     54                 }
     55             }
     56         }
     57     }
     58     if(pre[t] == -1) return false;
     59     else return true;
     60 }
     61 
     62 void mcmf()
     63 {
     64     while(spfa()){
     65         int _min = INF;
     66         for(int o = pre[t]; ~o; o = pre[e[o ^ 1].to]){
     67             _min = min(_min, e[o].cap - e[o].flow);
     68         }
     69         for(int o = pre[t]; ~o; o = pre[e[o ^ 1].to]){
     70             e[o].flow += _min;
     71             e[o ^ 1].flow -= _min;
     72         }
     73         //cout << _min << endl;
     74         //cout << "dis = " << d[t] << endl;
     75         a[++cnt] = d[t];
     76         //cout << "d = " << d[t] << endl;
     77     }
     78     for(int i = 1; i <= cnt; ++i) a[i] += a[i - 1];
     79 }
     80 
     81 ll GCD(ll a, ll b)
     82 {
     83     return b == 0 ? a : GCD(b, a % b);
     84 }
     85 
     86 void solve()
     87 {
     88     while(~scanf("%d%d", &n, &m)){
     89         cnt = 0;
     90         for(int i = 0; i <= n; ++i) head[i] = -1; tot = 0;
     91         int x, y, w;
     92         for(int i = 1; i <= m; ++i){
     93             scanf("%d%d%d", &x, &y, &w);
     94             add(x, y, w);
     95         }
     96         s = 1; t = n;
     97         mcmf();
     98         
     99         int q;
    100         scanf("%d", &q);
    101         while(q--){
    102             int x, y;
    103             scanf("%d%d", &x, &y);
    104             if(x == 0){
    105                 puts("NaN");
    106                 continue;
    107             }
    108             int gcd = GCD(x, y);
    109             y /= gcd; x /= gcd;
    110 
    111             int need = y / x + (y % x != 0);
    112             if(need > cnt){
    113                 puts("NaN");
    114                 continue;       
    115             }
    116 
    117             need = y / x;
    118             ll up = (ll)a[need] * x;
    119             ll down = y;
    120             int remains = y % x;
    121             up += (ll)(a[need + 1] - a[need]) * remains;
    122             gcd = GCD(up, down);
    123             up /= gcd; down /= gcd;
    124             printf("%lld/%lld
    ", up, down);
    125         }
    126     }
    127 }
    128 
    129 int main(){
    130 
    131     solve();
    132 
    133     return 0;
    134 }
  • 相关阅读:
    手机微信浏览器手动清楚浏览器缓存
    CSS 图像精灵怎么用(css sprites定位技术) 图标定位
    电商平台应该分析哪些数据?具体怎么去分析?
    酷家乐装修方案
    字体识别(根据图片截图识别字体) 求字体
    酿造系列:自己酿造夏日饮用的低度甜酒好喝吗?如何酿造?
    xcode Xcode_9.2.xip 官方离线下载地址
    nginx给server增加日志配置
    用monit监控mongodb,崩溃后自动重启mongdb
    电商产品经理进阶一:电商基础流程
  • 原文地址:https://www.cnblogs.com/SSummerZzz/p/13300870.html
Copyright © 2011-2022 走看看