BZOJ 1834 网络扩容 最大流+最小费用流

题目链接:

https://www.lydsy.com/JudgeOnline/problem.php?id=1834

题目大意:

给定一张有向图,每条边都有一个容量C和一个扩容费用W。这里扩容费用是指将容量扩大1所需的费用。
求: 
1、在不扩容的情况下,1到N的最大流; 
2、将1到N的最大流增加K所需的最小扩容费用。

思路:

第一问直接求费用流,第二问,在第一问的残余网络上,对于每条边额外加上INF容量费用为w的边,限制最大流量为k,也就是在0-1之间连边,容量为s,费用为0,然后跑一遍最小费用流就可以了。

  1 #include<bits/stdc++.h>
  2 #define IOS ios::sync_with_stdio(false);//不可再使用scanf printf
  3 #define Max(a, b) ((a) > (b) ? (a) : (b))//禁用于函数,会超时
  4 #define Min(a, b) ((a) < (b) ? (a) : (b))
  5 #define Mem(a) memset(a, 0, sizeof(a))
  6 #define Dis(x, y, x1, y1) ((x - x1) * (x - x1) + (y - y1) * (y - y1))
  7 #define MID(l, r) ((l) + ((r) - (l)) / 2)
  8 #define lson ((o)<<1)
  9 #define rson ((o)<<1|1)
 10 #define Accepted 0
 11 #pragma comment(linker, "/STACK:102400000,102400000")//栈外挂
 12 using namespace std;
 13 inline int read()
 14 {
 15     int x=0,f=1;char ch=getchar();
 16     while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
 17     while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
 18     return x*f;
 19 }
 20 typedef long long ll;
 21 const int MOD = 1000000007;//const引用更快,宏定义也更快
 22 const double eps = 1e-10;
 23 const double pi = acos(-1);
 24 const int INF = 0x3f3f3f3f;
 25 const int maxn = 10000 + 10;
 26 struct edge
 27 {
 28     int u, v, c, f, cost;
 29     edge(int u, int v, int c, int f, int cost):u(u), v(v), c(c), f(f), cost(cost){}
 30 };
 31 vector<edge>e;
 32 vector<int>G[maxn];
 33 int a[maxn];//找增广路每个点的水流量
 34 int p[maxn];//每次找增广路反向记录路径
 35 int d[maxn];//SPFA算法的最短路
 36 int inq[maxn];//SPFA算法是否在队列中
 37 
 38 void addedge(int u, int v, int c, int cost)
 39 {
 40     e.push_back(edge(u, v, c, 0, cost));
 41     e.push_back(edge(v, u, 0, 0, -cost));
 42     int m = e.size();
 43     G[u].push_back(m - 2);
 44     G[v].push_back(m - 1);
 45 }
 46 bool bellman(int s, int t, int& flow, long long & cost)
 47 {
 48     for(int i = 0; i <= t + 1; i++)d[i] = INF;//Bellman算法的初始化
 49     memset(inq, 0, sizeof(inq));
 50     d[s] = 0;inq[s] = 1;//源点s的距离设为0,标记入队
 51     p[s] = 0;a[s] = INF;//源点流量为INF(和之前的最大流算法是一样的)
 52 
 53     queue<int>q;//Bellman算法和增广路算法同步进行,沿着最短路拓展增广路,得出的解一定是最小费用最大流
 54     q.push(s);
 55     while(!q.empty())
 56     {
 57         int u = q.front();
 58         q.pop();
 59         inq[u] = 0;//入队列标记删除
 60         for(int i = 0; i < G[u].size(); i++)
 61         {
 62             edge & now = e[G[u][i]];
 63             int v = now.v;
 64             if(now.c > now.f && d[v] > d[u] + now.cost)
 65                 //now.c > now.f表示这条路还未流满(和最大流一样)
 66                 //d[v] > d[u] + e.cost Bellman 算法中边的松弛
 67             {
 68                 d[v] = d[u] + now.cost;//Bellman 算法边的松弛
 69                 p[v] = G[u][i];//反向记录边的编号
 70                 a[v] = min(a[u], now.c - now.f);//到达v点的水量取决于边剩余的容量和u点的水量
 71                 if(!inq[v]){q.push(v);inq[v] = 1;}//Bellman 算法入队
 72             }
 73         }
 74     }
 75     if(d[t] == INF)return false;//找不到增广路
 76     flow += a[t];//最大流的值,此函数引用flow这个值,最后可以直接求出flow
 77     cost += (long long)d[t] * (long long)a[t];//距离乘上到达汇点的流量就是费用
 78     for(int u = t; u != s; u = e[p[u]].u)//逆向存边
 79     {
 80         e[p[u]].f += a[t];//正向边加上流量
 81         e[p[u] ^ 1].f -= a[t];//反向边减去流量 (和增广路算法一样)
 82     }
 83     return true;
 84 }
 85 int MincostMaxflow(int s, int t, long long & cost)
 86 {
 87     cost = 0;
 88     int flow = 0;
 89     while(bellman(s, t, flow, cost));//由于Bellman函数用的是引用,所以只要一直调用就可以求出flow和cost
 90     return flow;//返回最大流,cost引用可以直接返回最小费用
 91 }
 92 int u[maxn], v[maxn], c[maxn], w[maxn];
 93 int main()
 94 {
 95     IOS;
 96     int n, m, k;
 97     cin >> n >> m >> k;
 98     for(int i = 1; i <= m; i++)cin >> u[i] >> v[i] >> c[i] >> w[i];
 99     for(int i = 1; i <= m; i++)addedge(u[i], v[i], c[i], 0);
100     ll cost;
101     cout<<MincostMaxflow(1, n, cost)<<" ";
102     addedge(0, 1, k, 0);
103     addedge(n, n + 1, k, 0);
104     for(int i = 1; i <= m; i++)addedge(u[i], v[i], INF, w[i]);
105     MincostMaxflow(0, n + 1, cost);
106     cout<<cost<<endl;
107     return Accepted;
108 }

猜你喜欢

转载自www.cnblogs.com/fzl194/p/9746239.html