• zkw费用流


    因为某些原因被迫学习

    我们回顾普通费用流的做法,通过每次spfa找到最小费用的流.
    但是每次只松弛一条路径,效率低下.
    于是就有了类似最大流多路增广的算法------zkw费用流

    算法1

    使用spfa把S到所有点的距离全部算出来
    然后满足在最短路上的点一定满足(dis[u]+length(u,v)=dis[v])
    根据这个条件,可以写出一个较优秀的算法

    bool spfa() {
    	memset(dis, 127, sizeof(dis)); 
            memset(vis, 0, sizeof(vis));
    	dis[S] = 0; q.push(S);
    	while (!q.empty()) {
    		int u = q.front(); q.pop(); in[u] = 0;
    		for (int i = lst[u]; i; i = g[i].nxt)
    			if (g[i].w && dis[g[i].to] > dis[u] + g[i].v) {
    				dis[g[i].to] = dis[u] + g[i].v;
    				if (!in[g[i].to]) in[g[i].to] = 1, q.push(g[i].to);
    			}
    	}
    	return dis[T] != dis[0];
    }
    LL ans;
    int dfs(int u, int flow) {
    	if (u == T) {
    		ans += dis[T] * flow;
    		return flow;
    	}
    	vis[u] = 1; int fl = 0;
    	for (int &i = cur[u]; i; i = g[i].nxt) {
    		int v = g[i].to; if (vis[v]) continue;
    		if (g[i].w && dis[v] == dis[u] + g[i].v) {
    			int d = dfs(v, min(flow, g[i].w));
    			g[i].w -= d; g[i ^ 1].w += d; fl += d;
    			flow -= d; if (!flow) break;
    		}
    	}
    	return fl;
    }
    void Mcmf(){
          while (spfa()) {
    		for (int i = 1; i <= T; i++)
    			cur[i] = lst[i];
    		dfs(S, 2147483647);
    	}    
    }
    

    算法2

    上述代码虽然效率不错,但是一些毒瘤题或者骗分还是跑不过.
    我们发现,算法的瓶颈在于spfa?
    一次增广后,(S->T)的最短路会发生一些变化(一些边被流满惹)
    这时通过再一次跑(dfs)可以把(S)能到的点找出来,然后被流满的边就是这个点集边缘的那些边.
    我们就可以快速更新最短路了.
    具体实现是维护(T)到其他点的最短路,然后松弛点集中的点.

    bool spfa() { 
        queue<int> que; 
        for (int i = 0; i <= N; i++) dis[i] = INF; 
        dis[T] = 0, inq[T] = 1, que.push(T); 
        while (!que.empty()) { 
    		int x = que.front(); que.pop(); 
    		for (int i = fir[x]; ~i; i = e[i].next) { 
    			int v = e[i].to, w = e[i ^ 1].cost; 
    			if (e[i ^ 1].cap && dis[v] > dis[x] + w) { 
    				dis[v] = dis[x] + w; 
    				if (!inq[v]) que.push(v), inq[v] = 1; 
    			} 
    		} 
    		inq[x] = 0; 
        } 
        return dis[S] != INF; 
    } 
    int tim, vis[MAX_N]; 
    bool relabel() { 
        int res = INF; 
        for (int x = 0; x <= N; x++) { 
    		if (vis[x] != tim) continue; 
    		for (int i = fir[x]; ~i; i = e[i].next) { 
    			int v = e[i].to; 
    			if (e[i].cap && vis[v] != tim) res = min(res, dis[v] + e[i].cost - dis[x]); 
    		} 
        } 
        if (res == INF) return 0; 
        for (int i = 0; i <= N; i++) if (vis[i] == tim) dis[i] += res; 
        return 1; 
    } 
    int dfs(int x, int f) { 
        if (x == T) return f; 
        vis[x] = tim; 
        int res = 0; 
        for (int &i = cur[x]; ~i; i = e[i].next) { 
    		int v = e[i].to, w = e[i].cost; 
    		if (e[i].cap && dis[x] == dis[v] + w && vis[v] != tim) { 
    			int d = dfs(v, min(f, e[i].cap)); 
    			res += d, f -= d; 
    			e[i].cap -= d, e[i ^ 1].cap += d; 
    			if (!f) break; 
    		} 
        } 
        return res; 
    } 
    void zkw() { 
        spfa(); 
        int flow = 0, res = 0; 
        do { 
    	int f = 0; 
    	do { 
                for (int i = 0; i <= N; i++)
                    cur[i] = fir[i];
    	    ++tim; 
                f = dfs(S, INF); 
    	    flow += f, res += dis[S] * f; 
    	} while (f); 
        } while (relabel());
        printf("%d %d
    ", flow, res); 
    } 
    

    代码是蒯的

  • 相关阅读:
    CMake入门
    医学图像中的窗宽、窗位
    移动端侧滑
    html5向左滑动删除特效
    同意条款按钮可用
    jquery 日历插件datepicker格式调整
    复选框美化
    select下拉框美化
    单选、复选框判断是否选中
    点击清空文本框中输入的值
  • 原文地址:https://www.cnblogs.com/zzy2005/p/13027350.html
Copyright © 2020-2023  润新知