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;
}


浙公网安备 33010602011771号