2279. 网络战争(最小割,01分数规划,二分)

2024-02-27 17:04

本文主要是介绍2279. 网络战争(最小割,01分数规划,二分),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

活动 - AcWing

给出一个带权无向图 G=(V,E),每条边 e 有一个权 we。

求将点 s 和点 t 分开的一个边割集 C,使得该割集的平均边权最小,即最小化:

∑(e∈C)we/|C|

注意: 边割集的定义与最小割中的割边的集合不同。在本题中,一个边割集是指:将这些边删去之后,s 与 t 不再连通。

输入格式

第一行包含四个整数 n,m,s,t,其中 n,m 分别表示无向图的点数、边数。

接下来 m 行,每行包含三个整数 a,b,w,表示点 a 和 b 之间存在一条无向边,边权为 w。

点的编号从 1 到 n。

输出格式

输出一个实数,表示将点 s 和点 t 分开的边割集的最小平均边权。

结果保留两位小数。

数据范围

2≤n≤100,
1≤m≤400,
1≤w≤107
保证 s 和 t 之间连通。

输入样例:
6 8 1 6
1 2 3
1 3 3
2 4 2
2 5 2
3 4 2
3 5 2
5 6 3
4 6 3
输出样例:
2.00

解析: 

01分数规划:上一篇题解有详细讲解,361. 观光奶牛(小数二分,spfa判断正环,01分数规划)-CSDN博客。

 

本题要求的是∑(e∈C)we/|C| 的最小值。这种形式的问题基本都需要用到 01 分数规划。

现在取一个值 x,然后我们看一下 ∑(e∈C)we/|C| 和 x 的关系。

假设现在 ∑(e∈C)we/|C|>x,通过变形得到 ∑(e∈C)we−|C|⋅x>0,由于 ∑e∈Cwe 和 x 都有 |C| 个,因此进一步化简为 ∑e∈C(we−x)>0。

假设现在 ∑e∈Cwe|C|<x,同样可以得到 ∑e∈C(we−x)<0。

因此我们可以根据 ∑e∈C(we−x) 和 0 的关系来判断 ∑e∈Cwe|C| 和 x 的关系。而这个关系是有二段性的,是可以进行二分的。

在整个范围区间内进行二分,二分出的中间值就是 x,如果 ∑e∈C(we−x)>0,说明∑e∈Cwe|C|>x,继续二分右区间。否则说明 ∑e∈Cwe|C|<x,继续二分左区间。最终得到一个固定值,就是答案。

每次需要求出 ∑e∈C(we−x),我们可以建一个新图,将所有边都减去 x,在新图中求一个边割集的权值和就是 ∑e∈C(we−x)。

那么新图中可能存在 we−x≤0,对于这样的边,我们一定要选上,因为一个边割集若加上一些无法让图重新连上的边,它仍然是一个边割集,但是权值和会变小,所以这种负权值边必选。

现在已经选上所有非正边,我们需要考虑一下剩下的边该怎么选,因为边割集和流网络的割是不一样的,边割集在有流网络的割的所有边的同时,在这两个集合里面还有一些边,这些边去掉之后也能让整张图不连通,由于剩下要考虑的边都是正边,我们要让权值和越小,因此这两个集合里面的边尽量能不选就不选,因此在权值和最小的情况下,边割集一定不包含所有集合里面的边了,这时就是只剩下两个集合之间的边了,而这些边的权值和其实就是流网络的割。

通过以上的分析,我们成功将每个边割集的权值和与流网络的割的边的容量和对应起来。而边割集的最小权值和就是割的最小容量和,即最小割。

然后本题是无向图,而流网络中是有向图,我们需要将有向图和无向图对应起来,我们只需要正常建两条有向边,来回的流量会被抵消,而且流网络的割有一个特点就是保证正向边是满流反向边是 0 流,所以无向图的割等价于有向图的割,不需要额外处理。

然后这里每条无向边建两条有向边,有向边在残量网络中又有反向边,所以每条无向边都要建四条边,这里和前面的某一题一样直接合并成两条边即可。

最终得出本题算法,二分找最小值,每次求一遍最小割继续二分。

作者:小小_88
链接:https://www.acwing.com/solution/content/128176/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

 

#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<utility>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<math.h>
#include<map>
#include<sstream>
#include<deque>
#include<unordered_map>
#include<unordered_set>
#include<bitset>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
const int N = 1e4 + 5, M = 1e5 * 2 + 10, INF = 0x3f3f3f3f;
const double eps = 1e-8;
int n, m, S, T;
int h[N], e[M], w[M], ne[M], idx;
double f[M];
int q[N], d[N], cur[N];void add(int a, int b, int c) {e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;e[idx] = a, w[idx] = c, ne[idx] = h[b], h[b] = idx++;
}bool bfs() {int hh = 0, tt = 0;memset(d, -1, sizeof d);q[0] = S, d[S] = 0, cur[S] = h[S];while (hh <= tt) {int t = q[hh++];for (int i = h[t]; i != -1; i = ne[i]) {int j = e[i];if (d[j] == -1 && f[i]) {d[j] = d[t] + 1;cur[j] = h[j];if (j == T)return 1;q[++tt] = j;}}}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 = ne[i]) {int j = e[i];cur[u] = i;if (d[j] == d[u] + 1 && f[i]) {double t = find(j, min(limit - flow, f[i]));if (!t)d[j] = -1;f[i] -= t, f[i ^ 1] += t, flow += t;}}return flow;
}double dinic(double mid) {double ret = 0;for (int i = 0; i < idx; i += 2) {if (w[i] <= mid) {ret += w[i] - mid;f[i] = f[i ^ 1] = 0;}else f[i] = f[i ^ 1] = w[i] - mid;}double r = 0, flow;while (bfs())while (flow = find(S, INF))r += flow;return r + ret;
}int main() {cin >> n >> m >> S >> T;memset(h, -1, sizeof h);for (int i = 1,a,b,c; i <= m; i++) {scanf("%d%d%d", &a, &b, &c);add(a, b, c);}double l = 0, r = 1e7;double mid;while (r - l > eps) {mid = (r + l) / 2;if (dinic(mid) < 0)r = mid;else l = mid;}printf("%.2lf\n", r);return 0;
}

这篇关于2279. 网络战争(最小割,01分数规划,二分)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/753060

相关文章

Java实现任务管理器性能网络监控数据的方法详解

《Java实现任务管理器性能网络监控数据的方法详解》在现代操作系统中,任务管理器是一个非常重要的工具,用于监控和管理计算机的运行状态,包括CPU使用率、内存占用等,对于开发者和系统管理员来说,了解这些... 目录引言一、背景知识二、准备工作1. Maven依赖2. Gradle依赖三、代码实现四、代码详解五

hdu2241(二分+合并数组)

题意:判断是否存在a+b+c = x,a,b,c分别属于集合A,B,C 如果用暴力会超时,所以这里用到了数组合并,将b,c数组合并成d,d数组存的是b,c数组元素的和,然后对d数组进行二分就可以了 代码如下(附注释): #include<iostream>#include<algorithm>#include<cstring>#include<stack>#include<que

hdu2289(简单二分)

虽说是简单二分,但是我还是wa死了  题意:已知圆台的体积,求高度 首先要知道圆台体积怎么求:设上下底的半径分别为r1,r2,高为h,V = PI*(r1*r1+r1*r2+r2*r2)*h/3 然后以h进行二分 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#includ

动态规划---打家劫舍

题目: 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。 给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。 思路: 动态规划五部曲: 1.确定dp数组及含义 dp数组是一维数组,dp[i]代表

Linux 网络编程 --- 应用层

一、自定义协议和序列化反序列化 代码: 序列化反序列化实现网络版本计算器 二、HTTP协议 1、谈两个简单的预备知识 https://www.baidu.com/ --- 域名 --- 域名解析 --- IP地址 http的端口号为80端口,https的端口号为443 url为统一资源定位符。CSDNhttps://mp.csdn.net/mp_blog/creation/editor

hdu 2602 and poj 3624(01背包)

01背包的模板题。 hdu2602代码: #include<stdio.h>#include<string.h>const int MaxN = 1001;int max(int a, int b){return a > b ? a : b;}int w[MaxN];int v[MaxN];int dp[MaxN];int main(){int T;int N, V;s

poj 1258 Agri-Net(最小生成树模板代码)

感觉用这题来当模板更适合。 题意就是给你邻接矩阵求最小生成树啦。~ prim代码:效率很高。172k...0ms。 #include<stdio.h>#include<algorithm>using namespace std;const int MaxN = 101;const int INF = 0x3f3f3f3f;int g[MaxN][MaxN];int n

poj 1287 Networking(prim or kruscal最小生成树)

题意给你点与点间距离,求最小生成树。 注意点是,两点之间可能有不同的路,输入的时候选择最小的,和之前有道最短路WA的题目类似。 prim代码: #include<stdio.h>const int MaxN = 51;const int INF = 0x3f3f3f3f;int g[MaxN][MaxN];int P;int prim(){bool vis[MaxN];

poj 2349 Arctic Network uva 10369(prim or kruscal最小生成树)

题目很麻烦,因为不熟悉最小生成树的算法调试了好久。 感觉网上的题目解释都没说得很清楚,不适合新手。自己写一个。 题意:给你点的坐标,然后两点间可以有两种方式来通信:第一种是卫星通信,第二种是无线电通信。 卫星通信:任何两个有卫星频道的点间都可以直接建立连接,与点间的距离无关; 无线电通信:两个点之间的距离不能超过D,无线电收发器的功率越大,D越大,越昂贵。 计算无线电收发器D

poj 1734 (floyd求最小环并打印路径)

题意: 求图中的一个最小环,并打印路径。 解析: ans 保存最小环长度。 一直wa,最后终于找到原因,inf开太大爆掉了。。。 虽然0x3f3f3f3f用memset好用,但是还是有局限性。 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#incl