• 最小割练习


    网络战争

    与01分数规划相结合
    (dfrac{sum W_e}{|C|} < lambda) 对应 (sum(W_e - lambda) < 0)
    二分 (lambda) 找到 (dfrac{sum W_e}{|C|}) 的最小值
    (W_e - lambda le 0) 的边必选, 剩余的集合内的边一定不选,则边割集与流网络的割相对应, 求一个最小割即可

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N = 100 + 10;
    const int M = (400 + 10) * 2;
    const int INF = 1e9;
    const double eps = 1e-8;
    
    int n, m, S, T;
    struct Edge
    {
    	int to, nxt, w;
    	double flow;
    }line[M];
    int fist[N], idx;
    int cur[N], d[N];
    
    void add(int x, int y, int z)
    {
    	line[idx] = {y, fist[x], z, 0};
    	fist[x] = idx ++;
    	line[idx] = {x, fist[y], z, 0};
    	fist[y] = idx ++; 
    }
    
    bool bfs()
    {
    	queue<int> q;
    	memset(d, -1, sizeof d);
    	q.push(S), d[S] = 0, cur[S] = fist[S];
    	while(!q.empty())
    	{
    		int u = q.front(); q.pop();
    		for(int i = fist[u]; i != -1; i = line[i].nxt)
    		{
    			int v = line[i].to;
    			if(d[v] == -1 && line[i].flow > 0)
    			{
    				d[v] = d[u] + 1;
    				cur[v] = fist[v];
    				if(v == T) return 1;
    				q.push(v);
    			}
    		} 
    	} 
    	return 0;
    }
    
    double find(int u, double limit)
    {
    	if(u == T) return limit;
    	double flow = 0;
    	for(int i = cur[u]; i != -1 && flow < limit; i = line[i].nxt)
    	{
    		cur[u] = i;
    		int v = line[i].to;
    		if(d[v] == d[u] + 1 && line[i].flow > 0)
    		{
    			double t = find(v, min(line[i].flow, limit - flow));
    			if(t <= 0) d[v] = -1;
    			line[i].flow -= t;
    			line[i ^ 1].flow += t;
    			flow += t;
    		} 
    	}
    	return flow;
    }
    
    double dinic(double mid)
    {
    	double res = 0;
    	for(int i = 0; i < idx; i += 2)
    		if(line[i].w <= mid) 
    		{
    			res += line[i].w - mid;
    			line[i].flow = line[i ^ 1].flow = 0; 
    		}
    		else line[i].flow = line[i ^ 1].flow = line[i].w - mid;
    	double r = 0, flow;
    	while(bfs()) while(flow = find(S, INF)) r += flow;
    	return res + r;
    }
     
    int main()
    {
    	scanf("%d%d%d%d", &n, &m, &S, &T);
    	memset(fist, -1, sizeof fist);
    	for(int i = 1; i <= m; ++ i)
    	{
    		int a, b, c;
    		scanf("%d%d%d", &a, &b, &c);
    		add(a, b, c);
    	}
    	
    	double l = 0, r = 1e7;
    	while(r - l > eps)
    	{
    		double mid = (l + r) / 2;
    		if(dinic(mid) < 0) r = mid;
    		else l = mid;
    	}
    	
    	printf("%.2lf
    ", r);
    	return 0;
    } 
    

    最优标号

    每一位之间互不影响, 拆成每一位处理
    把集合分为两类,一类是当前位为 (0) 的点, 一类是当前位为 (1) 的点
    集合内部的点异或值为 (0) ,这些边不会对答案有贡献, 集合间的边异或值为 (1), 对答案有贡献
    为了最小化边的数量, 跑最小割即可
    建立虚拟源点和汇点

    • 从源点向当前位初始值为 (0) 的点连一条容量为 (+infty) 的边.(保证该点一定和 (s) 在一个集合)
    • 从当前位初始值为 (1) 的点向汇点连一条容量为 (+infty) 的边.(保证该点一定和 (t) 在一个集合)
    #include <bits/stdc++.h>
    using namespace std;
    
    #define x first
    #define y second
    typedef pair<int, int> PII; 
    typedef long long LL;
    const int N = 500 + 10;
    const int M = (3000 + N) * 2;
    const int INF = 1e9;
    
    int n, m, k, S, T;
    struct Edge
    {
    	int to, nxt, flow;
    }line[M]; 
    int fist[N], idx;
    int cur[N], d[N];
    int p[N];
    PII edges[3010];
    
    void add(int x, int y, int z)
    {
    	line[idx] = {y, fist[x], z};
    	fist[x] = idx ++;
    	line[idx] = {x, fist[y], z};
    	fist[y] = idx ++;
    }
    
    bool bfs()
    {
    	queue<int> q;
    	memset(d, -1, sizeof d);
    	q.push(S), d[S] = 0, cur[S] = fist[S];
    	while(!q.empty())
    	{
    		int u = q.front(); q.pop();
    		for(int i = fist[u]; i != -1; i = line[i].nxt)
    		{
    			int v = line[i].to;
    			if(d[v] == -1 && line[i].flow)
    			{
    				d[v] = d[u] + 1;
    				cur[v] = fist[v];
    				if(v == T) return 1;
    				q.push(v); 
    			}
    		} 
    	}
    	return 0;
    }
    
    int find(int u, int limit)
    {
    	if(u == T) return limit;
    	int flow = 0;
    	for(int i = cur[u]; i != -1 && flow < limit; i = line[i].nxt)
    	{
    		cur[u] = i;
    		int v = line[i].to;
    		if(d[v] == d[u] + 1 && line[i].flow)
    		{
    			int t = find(v, min(line[i].flow, limit - flow));
    			if(!t) d[v] = -1;
    			line[i].flow -= t;
    			line[i ^ 1].flow += t;
    			flow += t;
    		}
    	}
    	return flow;
    }
    
    LL dinic(int k)
    {
    	memset(fist, -1, sizeof fist);
    	idx = 0;
    	for(int i = 1; i <= m; ++ i)
    	{
    		int a = edges[i].x, b = edges[i].y;
    		add(a, b, 1);
    	}
    	for(int i = 1; i <= n; ++ i)
    		if(p[i] >= 0)
    		{
    			if(p[i] >> k & 1) add(i, T, INF);
    			else add(S, i, INF); 
    		}
    	int res = 0, flow;
    	while(bfs()) while(flow = find(S, INF)) res += flow;
    	return res;
    }
    
    int main()
    {
    	scanf("%d%d", &n, &m);
    	S = 0, T = n + 1;
    	memset(fist, -1, sizeof fist);
    	for(int i = 1; i <= m; ++ i) scanf("%d%d", &edges[i].x, &edges[i].y);
    	scanf("%d", &k);
    	memset(p, -1, sizeof p);
    	for(int i = 1; i <= n; ++ i)
    	{
    		int a, b;
    		scanf("%d%d", &a, &b);
    		p[a] = b;
    	}
    	LL res = 0;
    	for(int i = 0; i <= 30; ++ i) res += dinic(i) << i;
    	printf("%lld
    ", res); 
    	return 0;
    } 
    

    最大获利

    最大权闭合图做法
    中转站的权值设为负值, 用户群的权值设为正值, 从用户群向可以提供服务的中转站连边, 该问题转化为最大权闭合图

    • 从源点向权值为正的点连一条容量为权值的边
    • 从权值为负的点向汇点连一条容量为权值的绝对值的边
    • 原图内部的边的容量设为 (+infty)
    #include <bits/stdc++.h>
    using namespace std;
    
    const int N = 50000 + 5000 + 10;
    const int M = (N + 50000 * 2) * 2;
    const int INF = 1e9;
    
    int n, m, S, T;
    struct Edge
    {
    	int to, nxt, flow;
    }line[M];
    int fist[N], idx;
    int cur[N], d[N];
    
    void add(int x, int y, int z)
    {
    	line[idx] = {y, fist[x], z};
    	fist[x] = idx ++;
    	line[idx] = {x, fist[y], 0};
    	fist[y] = idx ++;	
    }
    
    bool bfs()
    {
    	queue<int> q;
    	memset(d, -1, sizeof d);
    	q.push(S), d[S] = 0, cur[S] = fist[S];
    	while(!q.empty())
    	{
    		int u = q.front(); q.pop();
    		for(int i = fist[u]; i != -1; i = line[i].nxt)
    		{
    			int v = line[i].to;
    			if(d[v] == -1 && line[i].flow)
    			{
    				d[v] = d[u] + 1;
    				cur[v] = fist[v];
    				if(v == T) return 1;
    				q.push(v);
    			}
    		} 
    	}
    	return 0;
    }
    
    int find(int u, int limit)
    {
    	if(u == T) return limit;
    	int flow = 0;
    	for(int i = cur[u]; i != -1 && flow < limit; i = line[i].nxt)	
    	{
    		cur[u] = i;
    		int v = line[i].to;
    		if(d[v] == d[u] + 1 && line[i].flow)
    		{
    			int t = find(v, min(line[i].flow, limit - flow));
    			if(!t) d[v] = -1;
    			line[i].flow -= t;
    			line[i ^ 1].flow += t;
    			flow += t;
    		}
    	}
    	return flow;
    }
    
    int dinic()
    {
    	int res = 0, flow;
    	while(bfs()) while(flow = find(S, INF)) res += flow;
    	return res;
    }
    
    int main()
    {
    	scanf("%d%d", &n, &m);
    	S = 0, T = n + m + 1;
    	memset(fist, -1, sizeof fist);
    	for(int i = 1; i <= n; ++ i) 
    	{
    		int x;
    		scanf("%d", &x);
    		add(m + i, T, x); 
    	} 
    	int res = 0;
    	for(int i = 1; i <= m; ++ i)
    	{
    		int a, b, c;
    		scanf("%d%d%d", &a, &b, &c);
    		res += c;
    		add(S, i, c);
    		add(i, a + m, INF);
    		add(i, b + m, INF);
    	}
    	printf("%d
    ", res - dinic());
    	return 0;
    }
    

    最大密度子图做法
    把用户群直接看做是一条边,转化成无向图,获益转化为边权, 建造费用的负值转化为点权
    目的是使得 (|V''| + |E'|) 最大, 与点和边带权的最大密度子图最大化的值 (|E'| + |V''| - g * |V'|) 对应, 即在 $ g = 0$ 时最大
    建图:

    • 从源点向每个基站连一条容量为 (U) 的边
    • 从每个基站向汇点连一条容量为 (2 * g - dv - 2 * pv + U) 的边
    • 两个基站之间的双向边的容量设为边权

    (c(S, T) = U * n + 2 * g * |V'| - 2 * |E'| - 2 * |V''| = U * n - 2 * |E'| - 2 * |V''|)
    找到了最小割和 (|V''| + |E'|) 之间的关系, 求最小割即可使目标值最大化

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N = 5000 + 10;
    const int M = (N * 2 + 50000 + 10) * 2;
    const int INF = 1e9;
    
    int n, m, S, T;
    struct Edge
    {
    	int to, nxt, flow;
    }line[M];
    int fist[N], idx;
    int cur[N], d[N];
    int dg[N], p[N];
    
    void add(int x, int y, int z1, int z2)
    {
    	line[idx] = {y, fist[x], z1};
    	fist[x] = idx ++;
    	line[idx] = {x, fist[y], z2};
    	fist[y] = idx ++;
    }
    
    bool bfs()
    {
    	queue<int> q;
    	memset(d, -1, sizeof d);
    	q.push(S), d[S] = 0, cur[S] = fist[S];
    	while(!q.empty())
    	{
    		int u = q.front(); q.pop();
    		for(int i = fist[u]; i != -1; i = line[i].nxt)
    		{
    			int v = line[i].to;
    			if(d[v] == -1 && line[i].flow)
    			{
    				d[v] = d[u] + 1;
    				cur[v] = fist[v];
    				if(v == T) return 1;
    				q.push(v);
    			}
    		}
    	}
    	return 0;
    }
    
    int find(int u, int limit)
    {
    	if(u == T) return limit;
    	int flow = 0;
    	for(int i = cur[u]; i != -1 && flow < limit; i = line[i].nxt)	
    	{
    		cur[u] = i;
    		int v = line[i].to;
    		if(d[v] == d[u] + 1 && line[i].flow)
    		{
    			int t = find(v, min(line[i].flow, limit - flow));
    			if(!t) d[v] = -1;
    			line[i].flow -= t;
    			line[i ^ 1].flow += t;
    			flow += t;
    		}
    	}
    	return flow;
    }
    
    int dinic()
    {
    	int res = 0, flow;
    	while(bfs()) while(flow = find(S, INF)) res += flow;
    	return res;
    } 
    
    int main()
    {
    	scanf("%d%d", &n, &m);
    	S = 0, T = n + 1;
    	memset(fist, -1, sizeof fist);
    	for(int i = 1; i <= n; ++ i) 
    	{
    		scanf("%d", &p[i]); 
    		p[i] *= -1;
    	}
    	for(int i = 1; i <= m; ++ i)
    	{
    		int a, b, c;
    		scanf("%d%d%d", &a, &b, &c);
    		add(a, b, c, c);
    		dg[a] += c, dg[b] += c;
    	}
    	int U = 0;
    	for(int i = 1; i <= n; ++ i) U = max(U, 2 * p[i] + dg[i]);
    	for(int i = 1; i <= n; ++ i)
    	{
    		add(S, i, U, 0);
    		add(i, T, U - 2 * p[i] - dg[i], 0); 
    	}
    	
    	printf("%d
    ", (U * n - dinic()) / 2);
    	return 0;
    }
    

    生活的艰辛

    最大密度子图
    最大化 (dfrac{|E'|}{|V'|}),转化成判断最大化 (|E'| - g * |V'|)(0) 的关系
    建图:

    • 从源点向所有点连一条容量为 (U) 的边
    • 从所有点向汇点连一条容量为 (2 * g - dv + U) 的边
    • 图中原有边的容量设为 (1)

    (c(S, T) = U * n + 2 * g * |V'| - 2 * |E'|)
    找到了割与 (|E'| - g * |V'|) 的关系, 求最小割即可

    #include <bits/stdc++.h>
    using namespace std;
    
    #define x first
    #define y second
    typedef pair<int, int> PII;
    
    const int N = 100 + 10;
    const int M = (N * 2 + 1000 + 10) * 2;
    const int INF = 1e9;
    const double eps = 1e-8;
    
    int n, m, S, T, U;
    struct Edge
    {
    	int to, nxt;
    	double flow;
    }line[M];
    int fist[N], idx;
    int cur[N], d[N];
    int dg[N];
    PII p[M];
    
    int ans;
    bool st[N];
    
    void add(int x, int y, double z1, double z2)
    {
    	line[idx] = {y, fist[x], z1};
    	fist[x] = idx ++;
    	line[idx] = {x, fist[y], z2};
    	fist[y] = idx ++;
    }
    
    bool bfs()
    {
    	queue<int> q;
    	memset(d, -1, sizeof d);
    	q.push(S), d[S] = 0, cur[S] = fist[S];
    	while(!q.empty())
    	{
    		int u = q.front(); q.pop();
    		for(int i = fist[u]; i != -1; i = line[i].nxt)
    		{
    			int v = line[i].to;
    			if(d[v] == -1 && line[i].flow > 0)
    			{
    				d[v] = d[u] + 1;
    				cur[v] = fist[v];
    				if(v == T) return 1;
    				q.push(v);
    			}
    		} 
    	}
    	return 0;
    }
    
    double find(int u, double limit)
    {
    	if(u == T) return limit;
    	double flow = 0;
    	for(int i = cur[u]; i != -1 && flow < limit; i = line[i].nxt)
    	{
    		cur[u] = i;
    		int v = line[i].to;
    		if(d[v] == d[u] + 1 && line[i].flow > 0)
    		{
    			double t = find(v, min(line[i].flow, limit - flow));
    			if(t <= 0) d[v] = -1;
    			line[i].flow -= t;
    			line[i ^ 1].flow += t;
    			flow += t; 
    		}
    	}
    	return flow;
    }
    
    double dinic(double g)
    {
    	memset(fist, -1, sizeof fist);
    	idx = 0;
    	for(int i = 1; i <= m; ++ i)
    		add(p[i].x, p[i].y, 1, 1);
    	for(int i = 1; i <= n; ++ i)
    	{
    		add(S, i, U, 0);
    		add(i, T, U + g * 2 - dg[i], 0);
    	}
    	double res = 0, flow;
    	while(bfs()) while(flow = find(S, INF)) res += flow;
    	return res; 
    }
    
    void dfs(int u)
    {
    	st[u] = 1;
    	if(u != S) ans ++;
    	for(int i = fist[u]; i != -1; i = line[i].nxt)
    	{
    		int v = line[i].to;
    		if(!st[v] && line[i].flow > 0) dfs(v);
    	}
    }
    
    int main()
    {
    	scanf("%d%d", &n, &m);
    	S = 0, T = n + 1;
    	
    	for(int i = 1; i <= m; ++ i)
    	{
    		int a, b;
    		scanf("%d%d", &a, &b);
    		dg[a] ++, dg[b] ++;
    		p[i] = {a, b};
    	}
    	
    	U = m;
    	
    	double l = 0, r = m;
    	while(r - l > eps)
    	{
    		double mid = (r + l) / 2;
    		double t = dinic(mid);
    		if(U * n - t > 0) l = mid;
    		else r = mid;
    	}
    	
    	dinic(l);
    	dfs(S);
    	
    	if(!ans) puts("1
    1");
    	else 
    	{
    		printf("%d
    ", ans);
    		for(int i = 1; i <= n; ++ i)
    			if(st[i])
    				printf("%d
    ", i);
    	} 
    	return 0; 
    }
    

    有向图破坏

    最小点权覆盖集
    如果有一条从 (a)(b) 的边, 那么 (W_a^-)(W_b^+) 必须选一个来删掉这条边
    那么考虑把每个点拆点构造一个二分图, 第一排点都是作为出点的点, 第二排点都是作为入点的点
    若有一条从 (a)(b) 的边, 则从 (a^-)(b^+) 连一条边无向边
    则该问题转化为一个最小点权覆盖问题

    • 从源点向入点连一条容量为 (W_i^+) 的边
    • 从出点向汇点连一条容量为 (W_i^-) 的边
    • 若有一条从 (a)(b) 的边, 则从 (b^+)(a^-) 连一条容量为 (+infty) 的边

    最终找到割边输出方案

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N = 100 * 2 + 10;
    const int M = (5000 + N + 10) * 2;
    const int INF = 1e9;
     
    int n, m, S, T;
    struct Edge
    {
    	int to, nxt, flow;
    }line[M];
    int fist[N], idx;
    int cur[N], d[N];
    bool st[N];
    
    void add(int x, int y, int z)
    {
    	line[idx] = {y, fist[x], z};
    	fist[x] = idx ++;
    	line[idx] = {x, fist[y], 0};
    	fist[y] = idx ++; 
    } 
    
    bool bfs()
    {
    	queue<int> q;
    	memset(d, -1, sizeof d);
    	q.push(S), d[S] = 0, cur[S] = fist[S];
    	while(!q.empty())
    	{
    		int u = q.front(); q.pop();
    		for(int i = fist[u]; i != -1; i = line[i].nxt)
    		{
    			int v = line[i].to;
    			if(d[v] == -1 && line[i].flow)
    			{
    				d[v] = d[u] + 1;
    				cur[v] = fist[v];
    				if(v == T) return 1;
    				q.push(v);
    			}
    		} 
    	} 
    	return 0;
    }
    
    int find(int u, int limit)
    {
    	if(u == T) return limit;
    	int flow = 0;
    	for(int i = cur[u]; i != -1 && flow < limit; i = line[i].nxt)
    	{
    		cur[u] = i;
    		int v = line[i].to;
    		if(d[v] == d[u] + 1 && line[i].flow)
    		{
    			int t = find(v, min(line[i].flow, limit - flow));
    			if(!t) d[v] = -1;
    			line[i].flow -= t;
    			line[i ^ 1].flow += t;
    			flow += t;
    		}
    	}
    	return flow;
    }
    
    int dinic()
    {
    	int res = 0, flow;
    	while(bfs()) while(flow = find(S, INF)) res += flow;
    	return res; 
    } 
    
    void dfs(int u)
    {
    	st[u] = 1;
    	for(int i = fist[u]; i != -1; i = line[i].nxt)
    	{
    		int v = line[i].to;
    		if(!st[v] && line[i].flow) dfs(v);
    	}
    }
    
    int main()
    {
    	scanf("%d%d", &n, &m);
    	S = 0, T = 2 * n + 1;
    	memset(fist, -1, sizeof fist);
    	for(int i = 1; i <= n; ++ i)
    	{
    		int w;
    		scanf("%d", &w);
    		add(S, i, w);
    	} 
    	for(int i = 1; i <= n; ++ i)
    	{
    		int w;
    		scanf("%d", &w);
    		add(n + i, T, w);
    	}
    	for(int i = 1; i <= m; ++ i)
    	{
    		int a, b;
    		scanf("%d%d", &a, &b);
    		add(b, a + n, INF);
    	}
    	
    	printf("%d
    ", dinic());
    	
    	dfs(S);
    	
    	int res = 0;
    	for(int i = 0; i < idx; i += 2)
    	{
    		int u = line[i ^ 1].to, v = line[i].to;
    		if(st[u] && !st[v]) res ++;
    	}
    	
    	printf("%d
    ", res);
    	for(int i = 0; i < idx; i += 2)
    	{
    		int u = line[i ^ 1].to, v = line[i].to;
    		if(st[u] && !st[v])  
    		{
    			if(u == S) printf("%d +
    ", v);
    			if(v == T) printf("%d -
    ", u - n);
    		} 
    	}
    	return 0;
    } 
    

    王者之剑

    最大点权独立集

    • 只能在偶数秒拿宝石
    • 不可能同时拿走相邻格子上的宝石,把格子看成点,相邻格子之间连一条边,将格子黑白染色,可以形成一个二分图,则我们选择的格子一定是二分图上的独立集
    • 两行两行走,对于任何一个独立集,可以构造出一种合法方案

    故所有合法方案等价于二分图的一个独立集, 求一个最大点权独立集即可

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N = 10000 + 10;
    const int M = (N + 5000 * 4 + 10) * 2;
    const int INF = 1e9; 
    const int dx[] = {-1, 1, 0, 0};
    const int dy[] = {0, 0, -1, 1};
    
    int n, m, S, T;
    struct Edge
    {
    	int to, nxt, flow;
    }line[M];
    int fist[N], idx;
    int cur[N], d[N];
    
    int get(int x, int y)
    {
    	return (x - 1) * m + y;	
    }
    
    void add(int x, int y, int z)
    {
    	line[idx] = {y, fist[x], z};
    	fist[x] = idx ++;
    	line[idx] = {x, fist[y], 0};
    	fist[y] = idx ++;	
    }
    
    bool bfs()
    {
    	queue<int> q;
    	memset(d, -1, sizeof d);
    	q.push(S), d[S] = 0, cur[S] = fist[S];
    	while(!q.empty())
    	{
    		int u = q.front(); q.pop();
    		for(int i = fist[u]; i != -1; i = line[i].nxt)
    		{
    			int v = line[i].to;
    			if(d[v] == -1 && line[i].flow)
    			{
    				d[v] = d[u] + 1;
    				cur[v] = fist[v];
    				if(v == T) return 1;
    				q.push(v);
    			}
    		}
    	}
    	return 0;
    }
    
    int find(int u, int limit)
    {
    	if(u == T) return limit;
    	int flow = 0;
    	for(int i = cur[u]; i != -1 && flow < limit; i = line[i].nxt)
    	{
    		cur[u] = i;
    		int v = line[i].to;
    		if(d[v] == d[u] + 1 && line[i].flow)
    		{
    			int t = find(v, min(line[i].flow, limit - flow));
    			if(!t) d[v] = -1;
    			line[i].flow -= t;
    			line[i ^ 1].flow += t;
    			flow += t;
    		}
    	}
    	return flow;
    }
    
    int dinic()
    {
    	int res = 0, flow;
    	while(bfs()) while(flow = find(S, INF)) res += flow;
    	return res;	
    }
    
    int main()
    {
    	scanf("%d%d", &n, &m);
    	S = 0, T = n * m + 1;
    	memset(fist, -1, sizeof fist);
    	
    	int tot = 0;
    	for(int i = 1; i <= n; ++ i)
    		for(int j = 1; j <= m; ++ j)
    		{
    			int w;
    			scanf("%d", &w);
    			if(i + j & 1) 
    			{
    				add(S, get(i, j), w);
    				for(int k = 0; k < 4; ++ k)
    				{
    					int x = i + dx[k], y = j + dy[k];
    					if(x >= 1 && x <= n && y >= 1 && y <= m)
    						add(get(i, j), get(x, y), INF);
    				}
    			}
    			else add(get(i, j), T, w);
    			tot += w;
    		}	
    	printf("%d
    ", tot - dinic());
    	return 0;
    }
    

    有线电视网络

    使得两个点不连通, 枚举这两个点作为源点和汇点
    要删去的是点,为了和割边产生联系,我们考虑拆点,删去某个点等价于把该点的入点到出点之间的边选为割边, 把不能被选为割边的边容量设为 (+infty)

    • 每个点的入点到出点连一条容量为 (1) 的边
    • 内部边不能被选为割边,它的容量设为 (+infty)

    最小割即为使源点和汇点不连通时,需要删去的最少点数

    #include <bits/stdc++.h>
    using namespace std;
     
    const int N = 50 * 2 + 10;
    const int M = (50 * 50 + 50 + 10) * 2;
    const int INF = 1e9;
    
    int n, m, S, T;
    struct Edge
    {
    	int to, nxt, flow;
    }line[M];
    int fist[N], idx;
    int cur[N], d[N];
    
    void add(int x, int y, int z)
    {
    	line[idx] = {y, fist[x], z};
    	fist[x] = idx ++;
    	line[idx] = {x, fist[y], 0};
    	fist[y] = idx ++;
    } 
    
    bool bfs()
    {
    	queue<int> q;
    	memset(d, -1, sizeof d);
    	q.push(S), d[S] = 0, cur[S] = fist[S];
    	while(!q.empty()) 
    	{
    		int u = q.front(); q.pop();
    		for(int i = fist[u]; i != -1; i = line[i].nxt)
    		{
    			int v = line[i].to;
    			if(d[v] == -1 && line[i].flow)
    			{
    				d[v] = d[u] + 1;
    				cur[v] = fist[v];
    				if(v == T) return 1;
    				q.push(v); 
    			}
    		}
    	} 
    	return 0;
    }
    
    int find(int u, int limit)
    {
    	if(u == T) return limit;
    	int flow = 0;	
    	for(int i = cur[u]; i != -1 && flow < limit; i = line[i].nxt)
    	{
    		cur[u] = i;
    		int v = line[i].to;
    		if(d[v] == d[u] + 1 && line[i].flow)
    		{
    			int t = find(v, min(line[i].flow, limit - flow));
    			if(!t) d[v] = -1;
    			line[i].flow -= t;
    			line[i ^ 1].flow += t;
    			flow += t;
    		}
    	}
    	return flow;
    }
    
    int dinic()
    {
    	int res = 0, flow;
    	while(bfs()) while(flow = find(S, INF)) res += flow;
    	return res;
    }
    
    int main()
    {
    	while(scanf("%d%d", &n, &m) == 2)
    	{
    		memset(fist, -1, sizeof fist);
    		idx = 0;
    		for(int i = 0; i < n; ++ i) add(i, n + i, 1);
    		for(int i = 1; i <= m; ++ i)
    		{
    			int a, b;
    			scanf(" (%d,%d)", &a, &b);
    			add(n + a, b, INF);
    			add(n + b, a, INF);
    		}
    		int res = n;
    		for(int i = 0; i < n; ++ i)
    			for(int j = 0; j < i; ++ j)
    			{
    				S = n + i, T = j;
    				for(int k = 0; k < idx; k += 2)
    				{
    					line[k].flow += line[k ^ 1].flow;
    					line[k ^ 1].flow = 0;
    				}
    				res = min(res, dinic());
    			}
    		printf("%d
    ", res);
    	}	
    	return 0;
    } 
    
  • 相关阅读:
    TP生成二维码插件
    day23 常用模块(中)
    day22 作业
    day22 常用模块(上)
    day21 模块与包+软件开发目录规范
    day20 作业
    day20 函数收尾+面向过程+模块
    day19 作业
    day19 生成器+函数递归
    day18 作业
  • 原文地址:https://www.cnblogs.com/ooctober/p/14428071.html
Copyright © 2020-2023  润新知