轉載請注明出處: 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; }