BZOJ 1486 洛谷 P3199 [HNOI2009] 最小圈

题目描述

考虑带权的有向图 G=(V,E)G=(V,E)G=(V,E) 以及 w:E→Rw:E\rightarrow Rw:E→R ,每条边 e=(i,j)(i≠j,i∈V,j∈V)e=(i,j)(i\neq j,i\in V,j\in V)e=(i,j)(i≠j,i∈V,j∈V) 的权值定义为 wi,jw_{i,j}wi,j​ ,令 n=∣V∣n=|V|n=∣V∣ 。 c=(c1,c2,⋯,ck)(ci∈V)c=(c_1,c_2,\cdots,c_k)(c_i\in V)c=(c1​,c2​,⋯,ck​)(ci​∈V) 是 GGG 中的一个圈当且仅当 (ci,ci+1)(1≤i<k)(c_i,c_{i+1})(1\le i<k)(ci​,ci+1​)(1≤i<k) 和 (ck,c1)(c_k,c_1)(ck​,c1​) 都在 EEE 中,这时称 kkk 为圈 ccc 的长度同时令 ck+1=c1c_{k+1}=c_1ck+1​=c1​ ,并定义圈 c=(c1,c2,⋯,ck)c=(c_1,c_2,\cdots,c_k)c=(c1​,c2​,⋯,ck​) 的平均值为 μ(c)=∑i=1kwci,ci+1/k\mu(c)=\sum\limits_{i=1}^{k} w_{c_i,c_{i+1}}/kμ(c)=i=1∑k​wci​,ci+1​​/k ,即 ccc 上所有边的权值的平均值。令 μ′(c)=Min(μ(c))\mu'(c)=Min(\mu(c))μ′(c)=Min(μ(c)) 为 GGG 中所有圈 ccc 的平均值的最小值。现在的目标是:在给定了一个图 G=(V,E)G=(V,E)G=(V,E) 以及 w:E→Rw:E\rightarrow Rw:E→R 之后,请求出 GGG 中所有圈 ccc 的平均值的最小值 μ′(c)=Min(μ(c))\mu'(c)=Min(\mu(c))μ′(c)=Min(μ(c))

输入输出格式

输入格式:

第一行2个正整数,分别为 nnn 和 mmm ,并用一个空格隔开,只用 n=∣V∣,m=∣E∣n=|V|,m=|E|n=∣V∣,m=∣E∣ 分别表示图中有 nnn 个点 mmm 条边。接下来m行,每行3个数 i,j,wi,ji,j,w_{i,j}i,j,wi,j​ ,表示有一条边 (i,j)(i,j)(i,j) 且该边的权值为 wi,jw_{i,j}wi,j​ 。输入数据保证图 G=(V,E)G=(V,E)G=(V,E) 连通,存在圈且有一个点能到达其他所有点。

输出格式:

请输出一个实数 μ′(c)=Min(μ(c))\mu'(c)=Min(\mu(c))μ′(c)=Min(μ(c)) ,要求输出到小数点后8位。

输入输出样例

输入样例#1:

4 5
1 2 5
2 3 5
3 1 5
2 4 3
4 1 3

输出样例#1:

3.66666667

输入样例#2:

2 2
1 2 -2.9
2 1 -3.1

输出样例#2:

-3.00000000

说明

对于100%的数据, n≤3000,m≤10000,∣wi,j∣≤107n\le 3000,m\le 10000,|w_{i,j}| \le 10^7n≤3000,m≤10000,∣wi,j​∣≤107

我们先用正常人的方法来描述这道题的题面

给出一个带权的有向图,求出这个带权有向图里面最小平均权值回路.

当然,像我这种蒟蒻是根本不知道最优解.

所以我们用一个高复杂度的方法求解.

既然要找平均值最小的回路,很显然直接找是很麻烦的 .    其实我也不知道该怎么找

所以我们可以用二分最小平均值来求解.

先设L,R 分别为二分的上界和下界.MID=(L+R)/2.

然后将所有的边减去一个MID.

显而易见,假如这个MID不是最小的平均值,那么在减去MID后,图中必然存在负权环.

再随手打一个SPFA判断负权环就能解决了.

需要注意的是二分的精读问题.

题目中给出的是保留8位小数,所有我们二分的时候最好再加个几位保证精读正确.

剩下的也没什么需要解释了,这样看来这道题是一道普及组难度 不错的省选题(不过这种方法听说容易被HACK掉).

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int MAXN=100001;
struct edge
{
	int next,node;
	double w;
}h[MAXN*4];
double mid;
int n,m,tot;
int Head[MAXN];
double Dis[MAXN];
bool flag=0,visit[MAXN];
void add(int u,int v,double w)
{
	h[++tot].next=Head[u];
	h[tot].node=v;
	h[tot].w=w;
	Head[u]=tot;
}
void SPFA(int x)
{
    if(flag)
    return ;
    visit[x]=1;
    for(int i=Head[x];i;i=h[i].next)
    {
        int v=h[i].node;
        double w=h[i].w-mid;
        if(Dis[v]>Dis[x]+w)
        {
            if(visit[v])
            {
                flag=1;
                return ;
            }
            Dis[v]=Dis[x]+w;
            SPFA(v);
            if(flag)
            return ;
        }
    }
    visit[x]=0;
}
bool check(double x)
{
	memset(Dis,0,sizeof(Dis));
	memset(visit,0,sizeof(visit));
	for(int i=1;i<=n;i++)
	{
		flag=0;
		SPFA(i);
		if(flag)
		return 1;
	}
	return 0;
}
int main()
{
	cin>>n>>m;
	for(int i=1;i<=m;i++)
	{
		int x,y;
		double z;
		cin>>x>>y>>z;
		add(x,y,z);
	}
	double l=-19260817,r=19260817;
	while(l+1e-10<r)
	{
		mid=(l+r)/2.0;
		if(check(mid))
		r=mid;
		else
		l=mid;
	}
	printf("%.8lf",l);
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值