01 分數規劃
阿新 • • 發佈:2020-07-23
P2868 Sightseeing Cows G
給出一個 \(n\) 個點 \(m\) 條的有點權與邊權的有向圖,求一個環使得環上的點權和除以邊權和最大。
最優比率環問題,顯然01分數規劃
環上第\(i\)個點點權為\(a_i\),第\(i\)條邊的邊權為\(b_i\)
\[\frac{\sum_{i=1}^nF_i}{\sum_{i=1}^mT_i}=ans\\ 對於01分數規劃,考慮二分.二分可以將最優化問題轉化為判定問題.如果二分出來mid為L,則問題轉化為是否存在\\ \frac{\sum F_i}{\sum T_i}>L~~~分母乘過去(T_i>0)\\ \sum(F_i-L*T_i)>0~~左式乘-1~~~\sum(L*T_i-F_i)<0\\ 問題轉化為求負環,對於入點為u_i,出點為v_i的邊e_i,把邊權看做mid*T[e_i]-F[u_i],判負環\\ 有負環則L=mid,否則R=mid \]
#include<cstdio> #include<queue> #include<iostream> #define maxm 5005 #define maxn 1002 using namespace std; inline int read() { int x = 0,f = 1; char s = getchar(); while(s < '0' || s > '9') {if(s == '-') f = -1;s = getchar();} while(s >= '0' && s <= '9') {x = x * 10 + s - '0';s = getchar();} return f * x; } int l,p,head[maxn],tot = 0,vis[maxn],num[maxn],a[maxn]; double d[maxn]; struct edge{ int to,next,dis; }e[maxm]; inline void add(int u,int v,int w){ e[++tot].to = v; e[tot].next = head[u]; e[tot].dis = w; head[u] = tot; } int check(double x){//找負環 queue<int> q; for(int i = 1;i <= l;i++){ q.push(i); d[i] = 0;vis[i] = num[i] = 1; } while(!q.empty()){ int u = q.front(); q.pop();vis[u] = 0; for(int i = head[u];i;i = e[i].next){ int v = e[i].to; double dis = (double)e[i].dis; if(d[v] > d[u] + x * dis - (double)a[u])//邊權為mid*Tim[e_i]-Fun[u_i] { d[v] = d[u] + x * dis - (double)a[u]; if(!vis[v]) { q.push(v); vis[v]=1; if(++num[v] >= l) return 1; } } } } return 0; } int main(){ l=read();p=read(); for(int i=1;i<=l;++i)a[i]=read(); for(int i=1;i<=p;++i) { int u=read(),v=read(),dis=read(); add(u,v,dis); } double L=0,R=5001,mid; while(R-L>1e-4) { mid=(L+R)/2; if(check(mid))L=mid; else R=mid; } printf("%.2lf",L); return 0; }