1. 程式人生 > >[HNOI 2009]最小圈

[HNOI 2009]最小圈

int hnoi 保留 平均值 ring pre html def mat

Description

對於一張有向圖,要你求圖中最小圈的平均值最小是多少,即若一個圈經過k個節點,那麽一個圈的平均值為圈上k條邊權的和除以k,現要求其中的最小值

Input

第一行2個正整數,分別為n和m

以下m行,每行3個數,表示邊連接的信息,

Output

一行一個數,表示最小圈的值,保留8位小數。

Sample Input

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

Sample Output

3.66666667

HINT

若設邊權為v,那麽n≤3000,m≤10000,v≤50000

題解

最小化平均值($01$分數規劃)。

使用二分求解。對於一個猜測的$mid$,只需判斷是否存在平均值小於$mid$的回路。

如何判斷?

假設存在一個包含$k$條邊的回路,回路上各邊權值為$w_1$ ,$w_2$ ,$...$,$w_k$ ,那麽平均值小於$midv意味著:

$$w_1 +w_2 +...+w_k <k×mid$$

即:

$$(w_1 -mid)+(w_2 -mid)+...+(w_k -mid)<0$$

換句話說,只要把邊$(a,b)$的權$w(a,b)$改成$w(a,b)-mid$,再判斷新圖中是否有負環即可。

存在負環,那麽之前的不等式滿足,即存在著更小的平均值,$r=mid$;不存在,$l=mid$。

 1 //It is made by Awson on 2017.10.9
 2 #include <set>
 3 #include <map>
 4 #include <cmath>
 5 #include <ctime>
 6 #include <cmath>
 7 #include <stack>
 8 #include <queue>
 9 #include <vector>
10 #include <string>
11 #include <cstdio>
12
#include <cstdlib> 13 #include <cstring> 14 #include <iostream> 15 #include <algorithm> 16 #define LL long long 17 #define Min(a, b) ((a) < (b) ? (a) : (b)) 18 #define Max(a, b) ((a) > (b) ? (a) : (b)) 19 #define sqr(x) ((x)*(x)) 20 using namespace std; 21 const double eps = 1e-9; 22 const int N = 3000; 23 const int M = 10000; 24 25 int n, m, u, v, c; 26 bool vis[N+5]; 27 double dist[N+5]; 28 struct tt { 29 int to, next; 30 double cost; 31 }edge[M+5]; 32 int path[N+5], top; 33 34 void add(int u, int v, double c) { 35 edge[++top].to = v; 36 edge[top].next = path[u]; 37 edge[top].cost = c; 38 path[u] = top; 39 } 40 bool dfs(int u, double dec) { 41 vis[u] = 1; 42 for (int i = path[u]; i; i = edge[i].next) 43 if (dist[edge[i].to] > dist[u]+edge[i].cost-dec) { 44 if (vis[edge[i].to]) return true; 45 dist[edge[i].to] = dist[u]+(double)edge[i].cost-dec; 46 if (dfs(edge[i].to, dec)) return true; 47 } 48 vis[u] = 0; 49 return false; 50 } 51 bool judge(double dec) { 52 for (int i = 1; i <= n; i++) { 53 memset(vis, 0, sizeof(vis)); 54 memset(dist, 0, sizeof(dist)); 55 if (dfs(i, dec)) return true; 56 } 57 return false; 58 } 59 void work() { 60 scanf("%d%d", &n, &m); 61 double L = 0, R = 0; 62 for (int i = 1; i <= m; i++) { 63 scanf("%d%d%d", &u, &v, &c); 64 add(u, v, c); 65 R = max(R, (double)c); 66 } 67 while (R-L >= eps) { 68 double mid = (L+R)/2.; 69 if (judge(mid)) R = mid; 70 else L =mid; 71 } 72 printf("%.8lf\n", (L+R)/2.); 73 } 74 int main() { 75 work(); 76 return 0; 77 }

[HNOI 2009]最小圈