SPFA判断负环

转载请注明出处: http://www.cnblogs.com/gufeiyang

 

 

题意: 有一个n个城市的国家, n在1000之内,现在有些牛想做一个旅行,即从一个点出发最后再回到这个点,牛每个城市是后会得到一个欢乐值,这个欢乐值只有在第一次到的时间才会有,一些城市之间有路,给出了走路所需要花费的时间。现在想求出最大的平均获得欢乐值。

 

思路:  这道题是求最大的平均欢乐值。  一开始就像到了分数规划, 但是后来犯了很多错误,主要错误有以下几个方面。

1、 分数规划的思想没有理解好,导致spfa时关系弄反。

2、图建错了, 忽视了一个城市可以走多次。

 下面说一下这道题的正解。 假设答案是ans

那么sum(p[i]) - sum(w[j])*ans ==0,现在有了一个ans,那么如果我想让ans变大, 那么我就要让左边的数尽量大。 如果左边的两个sum写反,那么就是让左边最小。选择一个就行。

如果让左边最小, 就是在一个环中找出是否存在小于0的环。 这是一个判断只需要一个SPFA判断就行。 但是这个图的点上也有权值。 可以证明这个环是一个简单环,因为如果是两个环的话, 大的要给小的分摊一点。 这样的话这个环就是点和边一样多了,因此当每向前加一条边就把那么点的欢乐之间去就行了。

剩下的就是因为这个图可能不是联通的, 那么我如何用一次SPFA就能找出负环呢。 答案是假如一个0点,这个点和所有点的边权值为0, 当SPFA时,如果u==0时, 那么v的欢乐值就不减了,其他情况下再减。

代码:

 

View Code
#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
#include <queue>
#include <cmath>
using namespace std;
const int N = 1005, M = 5010;
const double esp = 1e-4, INF = 1000000000;

struct EDGE
 {
     int u, v, next;
     double ww,w;
 }edge[M*2];

int num, head[N*2], n, m;
int cc[N*2];
double p[N];

void add(int u, int v, double w)
 {
     edge[num].u = u;
     edge[num].v = v;
     edge[num].ww = w;
     edge[num].next = head[u];
     head[u] = num++;
 }

void init()
 {
     int u, v;
     double  a;
     num = 0;
     memset(head, -1, sizeof(head));
     for(int i=1; i<=n; i++)
      {
          scanf("%lf", &p[i]);
          add(0, i, 0);
      }
     for(int i=1; i<=m; i++)
      {
          scanf("%d%d%lf", &u, &v, &a);
          add(u,v,a);
      }
 }

void update(double p)
 {
     for(int i=0; i<num; i++)
      edge[i].w = edge[i].ww*p;
 }

bool SPFA()
 {
     bool in[N] = {0};
     double d[N], w;
     int u, v;
     queue<int>q;
     for(int i=1; i<=n; i++)
      {
          d[i] = INF;
      }
     q.push(0);
     while(!q.empty())
      {
          u = q.front();
          q.pop();
          in[u] = 0;
          for(int i=head[u]; i!=-1; i=edge[i].next)
           {
               v = edge[i].v;
               if(u == 0)
                w = edge[i].w;
               else w = edge[i].w - p[v];
               if(d[v] > d[u]+w)
                {
                    d[v] = d[u]+w;
                    cc[v]++;
                    if(cc[v] > n)
                          return true;
                    if(!in[v])
                     {
                         in[v] = 1;
                         q.push(v);
                     }
                }
           }
      }
    return false;
 }

bool have()
 {
     memset(cc, 0, sizeof(cc));
     if(SPFA())
      return true;
     return false;
 }

void solve()
 {
     double left = 0, right = 1<<18, mid;
     while(right-left > esp)
      {
          mid = (left+right)/2;
          update(mid);
          if(have())
           {
               left = mid+esp;
           }
          else right = mid-esp;
      }
     printf("%.2f\n",left);
 }

int main()
 {
     while(scanf("%d%d", &n, &m) != EOF)
      {
          init();
          solve();
      }
     return 0;
 }

 

 

 

posted @ 2012-09-24 14:25  Gu Feiyang  阅读(3661)  评论(0)    收藏  举报