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∑kwci,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;
}