[HNOI2009] 最小圈
题目描述
考虑带权有向图 G = ( V , E ) G=(V,E) G=(V,E) 以及 w : E → R w:E\rightarrow \R w:E→R,每条边 e = ( i , j ) e=(i,j) e=(i,j)( i ≠ j i\neq j i=j, i , j ∈ V i, j\in V i,j∈V)的权值定义为 w i , j w_{i,j} wi,j。设 n = ∣ V ∣ n=|V| n=∣V∣。
c
=
(
c
1
,
c
2
,
⋯
,
c
k
)
c=(c_1,c_2,\cdots,c_k)
c=(c1,c2,⋯,ck)(
c
i
∈
V
c_i\in V
ci∈V)是
G
G
G 中的一个圈当且仅当
(
c
i
,
c
i
+
1
)
(c_i,c_{i+1})
(ci,ci+1)(
1
≤
i
<
k
1\le i<k
1≤i<k)和
(
c
k
,
c
1
)
(c_k,c_1)
(ck,c1) 都在
E
E
E 中。称
k
k
k 为圈
c
c
c 的长度,同时记
c
k
+
1
=
c
1
c_{k+1}=c_1
ck+1=c1,并定义圈
c
=
(
c
1
,
c
2
,
⋯
,
c
k
)
c=(c_1,c_2,\cdots,c_k)
c=(c1,c2,⋯,ck) 的平均值为
μ
(
c
)
=
1
k
∑
i
=
1
k
w
c
i
,
c
i
+
1
\mu(c)= \frac 1 k \sum\limits_{i=1}^{k} w_{c_i,c_{i+1}}
μ(c)=k1i=1∑kwci,ci+1
即
c
c
c 上所有边的权值的平均值。设
μ
′
(
G
)
=
min
c
μ
(
c
)
\mu'(G)=\min_c\mu(c)
μ′(G)=mincμ(c) 为
G
G
G 中所有圈
c
c
c 的平均值的最小值。
给定图 G = ( V , E ) G=(V,E) G=(V,E) 以及 w : E → R w:E\rightarrow \R w:E→R,求出 G G G 中所有圈 c c c 的平均值的最小值 μ ′ ( G ) \mu'(G) μ′(G)。
输入格式
第一行两个正整数,分别为 n n n 和 m m m,并用一个空格隔开。其中 n = ∣ V ∣ n=|V| n=∣V∣, m = ∣ E ∣ m=|E| m=∣E∣ 分别表示图中有 n n n 个点 和 m m m 条边。
接下来 m m m 行,每行三个数 i , j , w i , j i,j,w_{i,j} i,j,wi,j,表示有一条边 ( i , j ) (i,j) (i,j) 且该边的权值为 w i , j w_{i,j} wi,j,注意边权可以是实数。输入数据保证图 G = ( V , E ) G=(V,E) G=(V,E) 连通,存在圈且有一个点能到达其他所有点。
输出格式
一个实数 μ ′ ( G ) \mu'(G) μ′(G),要求精确到小数点后 8 8 8 位。
样例 #1
样例输入 #1
4 5
1 2 5
2 3 5
3 1 5
2 4 3
4 1 3
样例输出 #1
3.66666667
样例 #2
样例输入 #2
2 2
1 2 -2.9
2 1 -3.1
样例输出 #2
-3.00000000
提示
对于 100 % 100\% 100% 的数据, 2 ≤ n ≤ 3000 2\leq n\le 3000 2≤n≤3000, 1 ≤ m ≤ 10000 1\leq m\le 10000 1≤m≤10000, ∣ w i , j ∣ ≤ 1 0 7 |w_{i,j}| \le 10^7 ∣wi,j∣≤107, 1 ≤ i , j ≤ n 1\leq i, j\leq n 1≤i,j≤n 且 i ≠ j i\neq j i=j。
提示:本题存在 O ( n m ) O(nm) O(nm) 的做法,但是 O ( n m log n ) O(nm\log n) O(nmlogn) 的做法也可以通过。
题意
我们需要在一张有向图上找到一个"平均权值最小的一个圈"。
分析
我们观察圈的平均权值的定义
μ
(
c
)
=
1
k
∑
i
=
1
k
w
c
i
,
c
i
+
1
\mu(c)= \frac 1 k \sum\limits_{i=1}^{k} w_{c_i,c_{i+1}}
μ(c)=k1i=1∑kwci,ci+1
处理一下
μ
(
c
)
=
∑
i
=
1
n
W
i
k
\mu(c) = \sum_{i = 1}^{n} \frac{W_i}{k}
μ(c)=i=1∑nkWi
这种形式的分式,常见的做法(我知道的做法 )就是分式规划了。
我们设:
∑
i
=
1
k
W
i
k
≤
X
\sum_{i = 1}^{k} \frac{W_i}{k} \le X
i=1∑kkWi≤X
处理一下
∑
i
=
1
k
(
W
i
−
X
)
≤
0
\sum_{i = 1}^{k} (W_i - X) \le 0
i=1∑k(Wi−X)≤0
意味着我们以
W
i
−
X
W_i - X
Wi−X 为 权值建图,在图中如果找到了负环则说明上式成立。我们知道SPFA算法可以寻找负环(简单来说,如果一个点到起点的距离被重复更新,说明图中存在负环)。
时间复杂度: 我们需要跑整个图
N
M
N M
NM的复杂度,分数规划二分复杂度
l
o
g
(
W
∗
W
)
log(W*W)
log(W∗W), 总复杂度:
O
(
N
M
l
o
g
(
W
∗
W
)
)
O(NMlog(W*W))
O(NMlog(W∗W))
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
//#define int long long
#define double long double
#define ios ios::sync_with_stdio(false); cin.tie(0);cout.tie(0)
const int N = 1e5+5;
const double eps = 1e-10;
const int INF = 0x3f3f3f3f;
typedef pair<int, double > PID;
int n, m, vis[N];
double dis[N];
vector<PID> e[N];
bool spfa(int u, double x){
vis[u] = 1;
for(auto [v, w] : e[u]){
if(dis[v] > dis[u] + w - x){
dis[v] = dis[u] + w - x;
if(vis[v] || spfa(v, x)) return 1;
}
}
vis[u] = 0;
return 0;
}
bool check(double x){
memset(vis, 0, sizeof(vis));
for(int i = 1; i <= n; i++) dis[i] = INF;
for(int i = 1; i <= n; i++){
if(spfa(i, x)) return 1;
}
return 0;
}
void solve(){
cin >> n >> m;
int u, v;
double w;
for(int i = 1; i <= m; i++){
cin >> u >> v >> w;
e[u].push_back({v, w});
}
double l = -1e7, r = 1e7;
while(r - l > eps){
double mid = (l + r)/2;
if(check(mid)) r = mid;
else l = mid;
}
//cout << fixed << setprecision(10) << l <<" " << r << endl;
cout << fixed << setprecision(8) << (r) << endl;
}
signed main(){
ios; int _;
_ = 1; //cin >> _;
while(_--){
solve();
}
return 0;
}