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;
}
posted @ 2022-11-27 22:51  zyy2001  阅读(49)  评论(0编辑  收藏  举报