对于⼀个已知的Ans,更新Ans的条件为:
(P1 + P2 + …… + Pk) / (T1 + T2 + …… + Tk) > Ans
变形后:P1 – T1 * Ans + P2 – T2 * Ans + …… + Pk – Tk * Ans > 0
所以将Pi – Ti * Ans作为边权,⼆分Ans,跑最长路(因为要使等式左边⼤于0);注意存在正环的情况,⽤SPFA可判环,遇到环则return true(路径可⽆限延长所以⼀定⼤于0);另外注意精度,保留3位⼩数,那么⼆分的条件最好为while(r - l >0.0001)
spfa:垂死病中惊坐起
(手推样例推了半天没推出来....)
#include<cstdio> #include<algorithm> #include<queue> #include<cstring> #define ll long long #define db double using namespace std; const int N = 105; const db eps = 0.0000001; const db inf = 999999.9; int n,head[N],ct[N],cnt; db dis[N],tim[N][N]; bool vis[N]; struct edge { int nxt,to; db wei; } e[N * N]; void add(int a,int b,db c) { e[++cnt].nxt = head[a]; e[cnt].to = b; e[cnt].wei = c; head[a] = cnt; } bool spfa(db k) { memset(ct,0,sizeof(ct)); memset(vis,0,sizeof(vis)); for(int i = 1; i <= n; i++) dis[i] = -inf; queue<int> q; q.push(1); dis[1]= 0; while(q.size()) { int u = q.front(); q.pop(); vis[u] = false; ct[u]++; if(ct[u] == n) return true; for(int i = head[u]; i; i = e[i].nxt) { int v = e[i].to; db w = e[i].wei - k * tim[u][v]; if(dis[v] < dis[u] + w) { dis[v] = dis[u] + w; if(!vis[v]) { vis[v] = true; q.push(v); } } } } return dis[n] >= 0; } int main() { scanf("%d",&n); for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++) { db a; scanf("%lf",&a); if(a == 0) continue; add(i,j,a); } for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++) scanf("%lf",&tim[i][j]); double l = 0.0,r = 10000.0; while(r - l > eps) { db mid = (l + r)/2; if(spfa(mid)) l = mid; else r = mid; } printf("%.3lf",l); return 0; }