转载请注明出处: 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的欢乐值就不减了,其他情况下再减。
代码:

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