分数规划
分数规划用来求一个分式的极值。
形式化地,给定 \(a_i,b_i\),求 \(w_i\) 使得
\[\frac{\sum\limits_{i=1}^na_i\times w_i}{\sum\limits_{i=1}^nb_i\times w_i}
\]
取到最值。
求解
二分。
一下以求最大值为例。二分 mid,然后在 check 中有:
\[\begin{aligned} &\frac{\sum_{i=1}^n a_i\times w_i}{\sum_{i=1}^n b_i\times w_i}>mid\\ \Longrightarrow&\sum\limits_{i=1}^n a_i\times w_i-mid\times \sum\limits_{i=1}^n b_i\cdot w_i>0\\
\Longrightarrow&\sum\limits_{i=1}^n w_i\times(a_i-mid\times b_i)>0 \end{aligned}
\]
那么只要求出不等号左边的式子的最大值就行了。如果最大值比 \(0\) 要大,说明 mid 是可行的,否则不可行。
const int N=1e5+5;
int n;
double a[N],b[N];
inline bool chk(double x){
double s=0;
for(int i=1;i<=n;i++)
if(a[i]-x*b[i]>0)
s+=a[i]-mid*[i];
return s>0;
}
signed main(){
n=read();
for(int i=1;i<=n;i++)
a[i]=read();
for(int i=1;i<=n;i++)
b[i]=read();
double l=0,r=1e9;
while(r-l>eps){
double mid=(l+r)/2;
if(chk(mid))
l=mid;
else
r=mid;
}
printf("%.8lf",l);
return 0;
}
限制选择数量
把第 \(i\) 个物品的权值设为 \(a_i-mid\times b_i\),然后选最大的 \(n-k\) 个。
inline bool chk(double x){
double s=0;
for(int i=1;i<=n;i++)
c[i]=a[i]-mid*b[i];
sort(c+1,c+1+n);
for(int i=n;i>k;i--)
s+=c[i];
return s>0;
}
限制分母
考虑 01 背包,将 \(b_i\) 设为第 \(i\) 个物品的重量,\(a_i-mid\times b_i\) 设为第 \(i\) 个物品的价值,\(dp_{n,w}\) 即为答案。
inline bool chk(int x){
fill(f+1,f+1+w,-inf);
for(int i=1;i<=n;i++)
for(int j=w;j>=0;j--){
int k=min(w,j+b[i]);
f[k]=max(f[k],f[j]+a[i]-mid*b[i]);
}
return f[w]>0;
}
最优比率生成树
将边权设为 \(a_i-mid\times b_i\) 然后跑 MST 即可。
最优比率环
将所有边权 \(-mid\),跑 spfa 判负环即可。
inline bool spfa(int u,double x){
vis[u]=1;
for(auto[v,w]:G[u]){
if(dis[v]>dis[u]+w-x){
dis[v]=dis[u]+w-x;
if(vis[v] or spfa(v,x))
return true;
}
}
vis[u]=0;
return false;
}
inline bool chk(double x){
fill(vis+1,vis+1+n,0);fill(dis+1,dis+1+n,0);
for(int i=1;i<=n;i++)
if(spfa(i,x))
return true;
return false;
}