2279. 网络战争
题目链接
2279. 网络战争
给出一个带权无向图 \(G = (V,E)\),每条边 \(e\) 有一个权 \(w\_e\)。
求将点 \(s\) 和点 \(t\) 分开的一个边割集 \(C\),使得该割集的平均边权最小,即最小化:
\(\frac{\sum\limits_{e \in C}w_e}{|C|}\)
注意: 边割集的定义与最小割中的割边的集合不同。在本题中,一个边割集是指:将这些边删去之后,\(s\) 与 \(t\) 不再连通。
输入格式
第一行包含四个整数 \(n,m,s,t\),其中 \(n,m\) 分别表示无向图的点数、边数。
接下来 \(m\) 行,每行包含三个整数 \(a,b,w\),表示点 \(a\) 和 \(b\) 之间存在一条无向边,边权为 \(w\)。
点的编号从 \(1\) 到 \(n\)。
输出格式
输出一个实数,表示将点 \(s\) 和点 \(t\) 分开的边割集的最小平均边权。
结果保留两位小数。
数据范围
\(2 \le n \le 100\),
\(1 \le m \le 400\),
\(1 \le w \le 10^7\),
保证 \(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分数规划
设 $\frac{\sum\limits_{e \in C}w_e}{|C|}=\lambda $,如果 \(\lambda\leq 0\),即\(\sum\limits_{e \in C}w_e-\lambda\times |C|=\sum\limits_{e \in C}(w_e-\lambda)\leq 0\),则说明答案可能还能更小,否则说明答案大了,即可以二分 \(\lambda\),\(\color{red}{如何check?}\)对于权值 \(w_e-\lambda\leq 0\) 的边肯定是选上更优,故先把这部分边选上,而对于其他边则在满足要求的情况下越小越好,即最后只剩下边权非负的边,等价于求解最小割,因为把最小割 \(S\) 连向 \(T\) 的边都删除后 \(s\) 和 \(t\) 一定不连通,且涉及的非负边权和最小
- 时间复杂度:\(O(n^2mlogw)\)
代码
// Problem: 网络战争
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/2281/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
// %%%Skyqwq
#include <bits/stdc++.h>
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
template <typename T> void inline read(T &x) {
int f = 1; x = 0; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
x *= f;
}
const int N=105,M=805;
const double eps=1e-8,inf=1e8;
int n,m,S,T;
int h[N],e[M],ne[M],w[M],idx;
double f[M];
int d[N],hh,tt,q[N],cur[N];
void add(int a,int b,int c)
{
e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
bool bfs()
{
memset(d,-1,sizeof d);
d[S]=hh=tt=0;
q[0]=S;
cur[S]=h[S];
while(hh<=tt)
{
int x=q[hh++];
for(int i=h[x];~i;i=ne[i])
{
int y=e[i];
if(d[y]==-1&&f[i])
{
d[y]=d[x]+1;
cur[y]=h[y];
if(y==T)return true;
q[++tt]=y;
}
}
}
return false;
}
double dfs(int x,double limit)
{
if(x==T)return limit;
double flow=0;
for(int i=cur[x];~i&&flow<limit;i=ne[i])
{
cur[x]=i;
int y=e[i];
if(d[y]==d[x]+1&&f[i])
{
double t=dfs(y,min(f[i],limit-flow));
if(!t)d[y]=-1;
f[i]-=t,f[i^1]+=t,flow+=t;
}
}
return flow;
}
bool dinic(double x)
{
double res=0;
for(int i=0;i<idx;i+=2)
if(w[i]<=x)res+=w[i]-x,f[i]=f[i^1]=0;
else
f[i]=f[i^1]=w[i]-x;
double flow=0;
while(bfs())while((flow=dfs(S,inf))>0)res+=flow;
return res<=0;
}
int main()
{
memset(h,-1,sizeof h);
scanf("%d%d%d%d",&n,&m,&S,&T);
for(int i=1;i<=m;i++)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
add(x,y,z),add(y,x,z);
}
double l=1,r=1e7;
while(fabs(r-l)>eps)
{
double mid=(l+r)/2;
if(dinic(mid))r=mid;
else
l=mid;
}
printf("%.2lf",l);
return 0;
}