1. 程式人生 > >BZOJ 1486 洛谷 P3199 [HNOI2009] 最小圈

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;
}