POJ ~ 3621 ~ Sightseeing Cows (01分數規劃 + 最短路)
阿新 • • 發佈:2018-12-11
題意
給一個有向圖,點數為L,邊數為P,然後輸入L個點的點權F[i],接下來輸入P條邊(u->v邊權為w),求一個點權和比邊權和最大的環,求這個比值。
題解
假設點權和為X,邊權和為Y,X/Y=ans,求ans最大。
u->v邊權為w的邊,我們建邊F[v] - ans*w,可得,我們二分ans,如果對於當前列舉值 x 該函式值小於0,那麼x應該變大,如果該函式大於0,x應該變小。函式值小於0即,該圖存在存在負環,所以用spfa判負環即可。
①這個環一定是簡單環。(POJ的discuss裡面有證明)②因為題中沒說圖連通,所以SPFA開始要把所有點入隊(但是這個題資料較弱,所以直接以1點為起點也可以過)。
//#include<bits/stdc++.h> #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<vector> #include<queue> using namespace std; const int MAXN = 1e5 + 5; const int INF = 0x3f3f3f3f; struct Edge { int from, to; double dist; //起點,終點,距離 Edge(int u, int v, double w):from(u), to(v), dist(w) {} }; struct SPFA { int n, m; //結點數,邊數(包括反向弧) vector<Edge> edges; //邊表。edges[e]和edges[e^1]互為反向弧 vector<int> G[MAXN]; //鄰接表,G[i][j]表示結點i的第j條邊在edges陣列中的序號 bool vis[MAXN]; //是否在佇列中 double d[MAXN]; //Bellman-Ford int p[MAXN]; //上一條弧 int cnt[MAXN]; //進隊次數 void init(int n) { this->n = n; edges.clear(); for (int i = 0; i <= n; i++) G[i].clear(); } void AddEdge(int from, int to, double dist) { edges.push_back(Edge(from, to, dist)); m = edges.size(); G[from].push_back(m - 1); } bool spfa(int s) { //for (int i = 0; i <= n; i++) d[i] = INF; memset(vis, 0, sizeof(vis)); memset(cnt, 0, sizeof(cnt)); //d[s] = 0; vis[s] = true; queue<int> Q; //Q.push(s); for (int i = 1; i <= n; i++)//所有點入隊 { d[i] = 0; vis[i] = true; cnt[i]++; Q.push(i); } while (!Q.empty()) { int u = Q.front(); Q.pop(); vis[u] = false; for (int i = 0; i < G[u].size(); i++) { Edge& e = edges[G[u][i]]; if (d[u] < INF && d[e.to] > d[u] + e.dist) { d[e.to] = d[u] + e.dist; p[e.to] = G[u][i]; if (!vis[e.to]) { Q.push(e.to); vis[e.to] = true; if (++cnt[e.to] > n) return false;//有負環 } } } } return true;//沒有負環 } }solve; const double eps = 1e-7; int n, m, F[MAXN]; vector<Edge> G; bool check(double x) { solve.init(n); for (int i = 0; i < m; i++) { int u = G[i].from, v = G[i].to; double w = x*G[i].dist - F[G[i].from]; solve.AddEdge(u, v, w); } return !solve.spfa(1); } int main() { scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++) scanf("%d", &F[i]); for (int i = 0; i < m; i++) { int u, v, w; scanf("%d%d%d", &u, &v, &w); G.push_back(Edge(u, v, w)); } double l = 0, r = 10000; while (r-l > eps) { double mid = (l+r)/2; if (check(mid)) l = mid; else r = mid; } printf("%.2f\n", l); return 0; } /* 5 7 30 10 10 5 10 1 2 3 2 3 2 3 4 5 3 5 2 4 5 5 5 1 3 5 2 2 */