zoukankan      html  css  js  c++  java
  • 费用流

    给定一个网络(G =(V,E)),每条边除了有容量限制(c(u,v)),还有一个单位限制(w(u,v))

    ((u,v))的流量为(f(u,v))时,需要花费(f(u,v)×w(u,v))(w)也满足斜对称性,即(w(u,v) = -w(v,u))

    则该网路中总花费最小的最大流称为最小费用最大流,即在最大化(sum_{(s,v)in E}f(s,v))的前提下最小化(sum_{(u,v)in E}f(u,v)×w(u,v))

    费用

    我们定义一条边的费用(w(u,v))表示边((u,v))上单位流量的费用。也就是说,当边((u,v))的流量为(f(u,v))时,需要花费(f(u,v)×w(u,v))的费用。

    最小费用最大流

    网络流图中,花费最小的最大流被称为最小费用最大流,这也是接下来我们要研究的对象

    MCMF算法

    在最大流的EK算法求解最大流的基础上,把用bfs求解任意增广路改为用spfa求解单位费用之和最小的增广路即可

    相当于把(w(u,v))作为边权,在残余网络上求最短路

    struct qxx {
      int nex, t, v, c;
    };
    qxx e[M];
    int h[N], cnt = 1;
    void add_path(int f, int t, int v, int c) {
      e[++cnt] = (qxx){h[f], t, v, c}, h[f] = cnt;
    }
    void add_flow(int f, int t, int v, int c) {
      add_path(f, t, v, c);
      add_path(t, f, 0, -c);
    }
    int dis[N], pre[N], incf[N];
    bool vis[N];
    bool spfa() {
      memset(dis, 0x3f, sizeof(dis));
      queue<int>q;
      q.push(s), dis[s] = 0, incf[s] = INF, incf[t] = 0;
      while(q.size()) {
        int u = q.front();
        q.pop();
        vis[u] = 0;
        for (int i = h[u]; i; i = e[i].nex) {
          const int &v = e[i].t, &w = e[i].v, &c = e[i].c;
          if (!w || dis[v] <= dis[u] + c) continue;
          dis[v] = dis[u] + c, incf[v] = min(w, incf[u]), pre[v] = i;
          if (!vis[v]) q.push(v), vis[v] = 1;
        }
      }
      return incf[t];
    }
    int maxflow, mincost;
    void update() {
      maxflow += incf[t];
      for (int u = t; u != s; u = e[pre[u] ^ 1].t) {
        e[pre[u]].v -= incf[t], e[pre[u] ^ 1].v += incf[t];
        mincost += incf[t] * e[pre[u]].c;
      }
    }
    

    (dicnic)算法

    我们可以在(dinic)算法的基础上进行改进,把bfs求分层图改为用spfa(由于有负边权,所以不能直接用dijkstra)来求一条单位费用之和最小的路径,也就是把(w(u,v))当做边权然后在参与网络上求最短路,当然在dfs中也要略作修改。遮掩就可以求的网络流图的最小费用最大流了。

    如何建反向边?罪域一条边((u,v,w,c))(其中(w)(c)分别为容量和费用),我们建立正向边((u,v,w,c))和反向边((v,u,0,-c))(其中(-c)是使得从反向边经过时退回原来的费用)。

    优化:可以使用 Primal-Dual 原始对偶算法将 SPFA 改成 Dijkstra!反正我不会

    时间复杂度 :可以证明上界为(O(nmf)),其中(f)表示流量。

    #define B cout << "BreakPoint" << endl;
    #define O(x) cout << #x << " " << x << endl;
    #define O_(x) cout << #x << " " << x << " ";
    #define Msz(x) cout << "Sizeof " << #x << " " << sizeof(x)/1024/1024 << " MB" << endl;
    #include<cstdio>
    #include<cmath>
    #include<iostream>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    #include<set>
    #define LL long long
    const int inf = 1e9 + 9;
    const int N = 2e5 + 5;
    using namespace std;
    inline int read() {
    	int s = 0,w = 1;
    	char ch = getchar();
    	while(ch < '0' || ch > '9') {
    		if(ch == '-')
    			w = -1;
    		ch = getchar();
    	}
    	while(ch >= '0' && ch <= '9') {
    		s = s * 10 + ch - '0';
    		ch = getchar();
    	}
    	return s * w;
    }
    int n,m,tot = 1,head[N],cur[N],to[N],nxt[N],val[N],cost[N],dis[N],ret,INF;
    bool vis[N];
    void add(int u, int v, int w, int c) {
      to[++tot] = v,nxt[tot] = head[u],head[u] = tot,val[tot] = w,cost[tot] = c;
    }
    void addedge(int u, int v, int w, int c) { add(u,v,w,c),add(v,u,0,-c); }
    bool spfa(int s, int t) {
    	memset(dis,127,sizeof(dis));
    	INF = dis[0];
    	memcpy(cur,head,sizeof(head));
      	queue<int> q;
      	q.push(s),dis[s] = 0,vis[s] = 1;
      	while (!q.empty()) {
        	int u = q.front();
        	q.pop(),vis[u] = 0;
        	for (int i = head[u]; i; i = nxt[i]) {
          		int v = to[i];
          		if(val[i] && dis[v] > dis[u] + cost[i]) {
            		dis[v] = dis[u] + cost[i];
            		if(!vis[v]) q.push(v),vis[v] = 1;
          		}
        	}
      	}
      	return dis[t] != INF;
    }
    int dfs(int u, int t, int flow) {
    	if(u == t) return flow;
      	vis[u] = 1;
      	int ans = 0;
      	for(int i = cur[u];i && ans < flow;i = nxt[i]) {
        	int v = to[i];
       		if(!vis[v] && val[i] && dis[v] == dis[u] + cost[i]) {
          		int x = dfs(v, t, std::min(val[i],flow - ans));
          		if(x) ret += x * cost[i],val[i] -= x,val[i ^ 1] += x,ans += x;
        	}
      	}
      	vis[u] = 0;
      	return ans;
    }
    int mcmf(int s, int t) {
    	int ans = 0;
    	while(spfa(s, t)){
        	int x;
        	while((x = dfs(s,t,INF))) ans += x;
      	}
      	return ans;
    }
    int main() {
    	int s, t;
    	n = read(),m = read(),s = read(),t = read();
    	while (m--) {
        	int u = read(),v = read(),w = read(),c = read();
        	addedge(u,v,w,c);
      	}
    	int ans = mcmf(s,t);
      	printf("%d %d
    ",ans,ret);
      	return 0;
    }
    
    
  • 相关阅读:
    [数字信号处理]离散傅里叶变换及其性质
    [数字信号处理]序列的逆z变换
    [数字信号处理]序列的z变换
    [数字信号处理]从傅里叶级数到傅里叶变换
    [物理]简谐振动总结
    [数字信号处理]常系数差分方程
    [数字信号处理]时域离散系统
    [数字信号处理]入门基本概念
    团队作业6-复审与事后分析
    Alpha阶段项目复审
  • 原文地址:https://www.cnblogs.com/excellent-zzy/p/12373689.html
Copyright © 2011-2022 走看看