POJ3621 Sightseeing Cows 最短路求最优比率生成环

来源:互联网 发布:搞笑相机软件大全 编辑:程序博客网 时间:2024/05/16 19:41

题意:给定一个n(2 <= n <= 1000)个点,m(2 <= m <= 5000)条边的有向图,给定每个点的点值f(i)和每条边的权值w(i),求一个环使得路径上点权和除以边权和最大。


还是要推公式。

设最大值为ans

那么ans>=∑f(i)/∑w(i);

∑w(i)*ans>=∑f(i);

ans可以合到∑当中去。

所以∑(ans*w(i))>=∑f(i);

∑(ans*w(i))-∑f(i)>=0;

∑(ans*w(i)-f(i))>=0;

所以我们建边直接建立ans*w(i)-f(i);

之后我们看看有没有环,如果存在负环也不行。

而我们要输出的ans就是需要二分了。

二分之后建图,(i,j)的值就是ans*w(i,j)-f(j);

如果有负环,说明ans小了,需要变大一点,反之变小一点。

  1. #include <cstdio>  
  2. #include <queue>  
  3. #include <cstring>  
  4. using namespace std;  
  5.   
  6. bool inq[1005];  
  7. int n, m, e, x[5005], y[5005], z[5005], f[1005], hd[1005], cnt[1005];  
  8. double d[1005];  
  9.   
  10. struct Edge {  
  11.     int to, nxt;  
  12.     double w;  
  13. }edge[5005];  
  14.   
  15. void add(int x, int y, double z) {  
  16.     edge[++e].to = y;  
  17.     edge[e].w = z;  
  18.     edge[e].nxt = hd[x];  
  19.     hd[x] = e;  
  20. }  
  21.   
  22. bool spfa() {  
  23.     queue<int> q;  
  24.     for(int i = 1; i <= n; i++) q.push(i), d[i] = 0, inq[i] = 1;  
  25.     memset(cnt, 0, sizeof cnt);  
  26.     while(!q.empty()) {  
  27.         int u = q.front(); q.pop();  
  28.         inq[u] = 0;  
  29.         for(int i = hd[u]; i; i = edge[i].nxt) {  
  30.             Edge &v = edge[i];  
  31.             if(d[v.to] > d[u] + v.w) {  
  32.                 d[v.to] = d[u] + v.w;  
  33.                 if(++cnt[v.to] > n) return true;  
  34.                 if(!inq[v.to]) q.push(v.to), inq[v.to] = 1;  
  35.             }  
  36.         }  
  37.     }  
  38.     return false;  
  39. }  
  40.   
  41. bool ok(double ans) {  
  42.     e = 0;  
  43.     memset(hd, 0, sizeof hd);  
  44.     for(int i = 1; i <= m; i++) add(x[i], y[i], ans * z[i] - f[x[i]]);  
  45.     return spfa();  
  46. }  
  47.   
  48. int main() {  
  49.     scanf("%d%d", &n, &m);  
  50.     for(int i = 1; i <= n; i++) scanf("%d", &f[i]);  
  51.     for(int i = 1; i <= m; i++) scanf("%d%d%d", &x[i], &y[i], &z[i]);  
  52.     double l = 0, r = 1000;  
  53.     while(r - l > 1e-4) {  
  54.         double mid = (l+r) / 2;  
  55.         if(ok(mid)) l = mid;  
  56.         else r = mid;  
  57.     }  
  58.     printf("%.2f", l);  
  59.     return 0;  
  60. }