zoukankan      html  css  js  c++  java
  • 「THUSCH 2017」换桌(zkw费用流)—对几种费用流算法的总结

    https://loj.ac/problem/2979

    这个题直接建图(O((nm)^2))的边数,考虑对每个环加一些中转点,就变成了(O(n^2m+nm^2)),然后就是跑费用流了。

    先放出三个算法的submission:
    SPFA后单路增广:https://loj.ac/submission/781683
    SPFA后多路增广:https://loj.ac/submission/781676
    zkw多路增广:https://loj.ac/submission/781679
    在这个题中,只有zkw费用流才能跑过。
    upd:
    按这篇博客所说:https://www.cnblogs.com/Narh/p/10841141.html
    用线段树优化一下[l,r]的连边(感觉聊胜于无),SPFA后多路增广加上当前弧优化也能跑过。

    SPFA多路增广是由单路增广得来的,可以理解为把dinic的BFS换成了SPFA,
    但多路增广的dfs部分和dinic有点不同,加了不能走重点(防止在环上绕圈圈,dinic不会绕圈),和zkw的增广部分写法一样。

    不难发现,SPFA后多路增广复杂度不会劣于单路增广,在图比较密集时,优于单路增广。

    zkw多路增广与SPFA多路增广的不同在于:SPFA多路增广每次都重新求最短路,而zkw多路增广借用KM的顶标思想,一点点扩展最短路。
    当图比较密集(长度短)时,zkw优于SPFA多路增广,因为SPFA的复杂度还是摆在那里的,而zkw可以很快的找到新的最短路。
    但是,当图比较稀疏时,zkw多路增广可能要扩展很多次才能找到一条新的最短路,而SPFA多路增广只用一次就能找到至少一条最短路,这时SPFA就更快了。

    回到本题,图显然很密集,并且数据随机,按理说zkw多路增广也应该跑的最快。

    Code:


    #include<bits/stdc++.h>
    #define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)
    #define ff(i, x, y) for(int i = x, _b = y; i <  _b; i ++)
    #define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)
    #define ll long long
    #define pp printf
    #define hh pp("
    ")
    using namespace std;
    
    const int N = 2e6 + 5;
    
    int n, m, a[305][305], b[305][305], id[305][305];
    
    int fi[N], nt[N], to[N], r[N], w[N], tot = 1;
    
    void link(int x, int y, int z, int z2) {
    //	pp("%d %d %d %d
    " ,x, y, z, z2);
    	nt[++ tot] = fi[x], to[tot] = y, r[tot] = z, w[tot] = z2, fi[x] = tot;
    	nt[++ tot] = fi[y], to[tot] = x, r[tot] = 0, w[tot] = -z2, fi[y] = tot;
    }
    
    int S, T;
    
    int dis[N], bz[N];
    
    const int inf = 1e9;
    
    ll ans, ans2;
    
    int change() {
    	int mh = inf;
    	fo(i, 1, T) if(bz[i])
    		for(int j = fi[i]; j; j = nt[j]) if(!bz[to[j]] && r[j])
    			mh = min(mh, dis[to[j]] + w[j] - dis[i]);
    	if(mh == inf) return 0;
    	fo(i, 1, T) if(bz[i]) dis[i] += mh;
    	return 1;
    }
    
    int dg(int x, int flow) {
    	if(x == T) {
    		ans += dis[S] * flow;
    		ans2 += flow;
    		return flow;
    	}
    	bz[x] = 1;
    	int use = 0;
    	for(int i = fi[x]; i; i = nt[i]) if(!bz[to[i]] && r[i] && dis[x] == dis[to[i]] + w[i]) {
    		int t = dg(to[i], min(flow - use, r[i]));
    		use += t, r[i] -= t, r[i ^ 1] += t;
    		if(use == flow) return use;
    	}
    	return use;
    } 
    
    int main() {
    	scanf("%d %d", &n, &m);
    	fo(i, 1, n) fo(j, 1, m) scanf("%d", &a[i][j]);
    	fo(i, 1, n) fo(j, 1, m) scanf("%d", &b[i][j]);
    	fo(i, 1, n) fo(j, 1, m) id[i][j] = (i - 1) * m + j;
    	S = 3 * n * m + 1, T = S + 1;
    	fo(i, 1, n) fo(j, 1, m) {
    		link(S, id[i][j], 1, 0);
    		fo(k, a[i][j] + 1, b[i][j] + 1) {
    			link(id[i][j], 2 * n * m + id[k][j], 1, 2 * abs(i - k));
    		}
    		link(n * m + id[i][j], T, 1, 0);
    		fo(k, 1, m) {
    			int x = abs(j - k);
    			if(m - x < x) x = m - x;
    			link(2 * n * m + id[i][k], n * m + id[i][j], 1 << 30, x);
    		}
    	}
    	int sum = 0;
    	do {
    		do {
    			fo(i, 1, T) bz[i] = 0;
    		} while(dg(S, 1 << 30));
    	} while(change());
    	if(ans2 < n * m) pp("-1
    "); else
    		pp("%lld
    ", ans);
    }
    
  • 相关阅读:
    Docker部署大型互联网电商平台
    大数据开发你需要知道的十个技术
    详解Linux运维工程师高级篇(大数据安全方向).
    大数据小项目之电视收视率企业项目05
    大数据小项目之电视收视率企业项目04--完全分布式搭建
    大数据小项目之电视收视率企业项目03
    大数据小项目之电视收视率企业项目02
    大数据小项目之电视收视率企业项目01
    本地Navicat连接虚拟机MySQL
    centos7安装MySQL
  • 原文地址:https://www.cnblogs.com/coldchair/p/12640057.html
Copyright © 2011-2022 走看看