zoukankan      html  css  js  c++  java
  • [网络流]最大流

    网络流最大流最小割

    题目链接

    就是一道点割。

    先说边割

    边割比较常见。

    最大流

    最大流等于最小割,我懒得证。

    求最大流的思路就是每次尝试找一条从源点到汇点的通路,然后找到这条路上残余流量最小的流量,答案加上这个流量,这条通路上每条边的残余流量减去这个值,反向边加上这个值。

    关于反向边,实际上是一个反悔机制。反向流多少,表示正向已经流了多少。也就是说,如果我们从反向边流了一些流量,就相当于从这条边退回了一部分流量。

    点割

    所谓点割,就是被割掉的不再是边,而是点。思路是将点转化成边。

    如上题:将每个点拆成(i)(i+n)两个点,中间连一条流量为(1)的边,将这条边割掉,就相当于割掉这个点。

    详见代码。

    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <queue>
    using namespace std;
    long long read(){
    	long long x = 0; int f = 0; char c = getchar();
    	while(c < '0' || c > '9') f |= c == '-', c = getchar();
    	while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
    	return f ? -x:x;
    }
    
    const int INF = 2147483647;
    int n, m, s, t;
    struct szh{
    	int nxt, to, w;
    }a[4004];
    int head[203],cnt=1;
    void add(int x, int y, int w){
    	a[++cnt].nxt = head[x], a[cnt].to = y, head[x] = cnt, a[cnt].w = w;
    }
    
    int dis[203];
    bool bfs(){
    	memset(dis, 0, sizeof dis);
    	queue<int> q;
    	q.push(s), dis[s] = 1;
    	while(!q.empty()){
    		int u = q.front();q.pop();
    		for (int i = head[u], v; v = a[i].to, i; i = a[i].nxt)
    			if(a[i].w && !dis[v]) q.push(v), dis[v] = dis[u] + 1;
    	}
    	return dis[t];
    }
    
    int fir[103];
    int dfs(int u, int flow){
    	if(u == t || !flow) return flow;
    	int ans = 0;
    	for (int &i = fir[u], v; v = a[i].to, i; i = a[i].nxt)
            //弧优化
    		if(a[i].w && dis[v] == dis[u] + 1){
    			int x = dfs(v, min(flow, a[i].w));
    			a[i].w -= x, a[i^1].w += x, ans += x; //更新残余流量
    			if(!(flow-=x)) break;
    		}
    	return ans;
    }
    
    void dinic(){
    	int ans = 0;
    	while(bfs()){
    		for (int i = 1; i <= (n << 1); ++i) fir[i] = head[i];
            //为dfs中取址操作做铺垫
    		ans += dfs(s,INF);
    	}
    	printf("%d", ans);
    }
    
    int main(){
    	n = read(), m = read(), s = read(), t = read();
        s += n; //源点不能删掉
    	for (int i = 1; i <= n; ++i) add(i, i + n, 1), add(i + n, i, 0);
        //拆点
    	for (int i = 1; i <= m; ++i){
    		int x = read(), y = read();
    		add(x + n, y, INF), add(y, x + n, 0); //电脑之间不会断,所以连INF
    		add(y + n, x, INF), add(x, y + n, 0);
    	}
    	dinic(); //模板
    	return 0;
    }
    
  • 相关阅读:
    使用 Dockerfile 定制镜像
    UVA 10298 Power Strings 字符串的幂(KMP,最小循环节)
    UVA 11090 Going in Cycle!! 环平均权值(bellman-ford,spfa,二分)
    LeetCode Best Time to Buy and Sell Stock 买卖股票的最佳时机 (DP)
    LeetCode Number of Islands 岛的数量(DFS,BFS)
    LeetCode Triangle 三角形(最短路)
    LeetCode Swap Nodes in Pairs 交换结点对(单链表)
    LeetCode Find Minimum in Rotated Sorted Array 旋转序列找最小值(二分查找)
    HDU 5312 Sequence (规律题)
    LeetCode Letter Combinations of a Phone Number 电话号码组合
  • 原文地址:https://www.cnblogs.com/kylinbalck/p/10590252.html
Copyright © 2011-2022 走看看