分数规划
引入

分数规划主要是对形如
\(w\)数组表示选与不选,最值化上式,不过一般会要求选k个,或者总重量至少为\(W\)再加上一大堆乱七八糟的限制
——————————————————————
解决方法
一般来讲用二分法,还有一个\(Dinkelbach\) 算法算是对二分的优化,就是用上一次的答案来做左边界,不过我们没有必要学习它
P10505
在这可以写一下分数规划的基本原理
\(\frac{a}{b}>mid\Longrightarrow a-b*mid>0\)
找所有大于0的元素对累加即可
基本上所有的分数规划都是这个原理,而这个题让我们选择\(n-k\)个数,如果必须要选择负数那怎么办呢,考虑此时还满足相加吗,实际上是满足的(在\(mid<1\)时),读者感兴趣可以自己去证明
那这题式子很显然就是\(设 t_i=a_i-mid*b_i,取最大的几个即可\)
这就是我说的那种总重量至少为\(W\)的题,也不算恶心,\(check\)里面改成背包,注意这题的重量加和可能会很大,所以\(f_i\)定义为重量和至少为\(i\)的最优值,填表写感觉更好捏
bool check(double x) {
for(int i=1;i<=w;i++) f[i]=-P;
for(int i=1;i<=n;i++) {
t[i]=a[i]-b[i]*1.0*x;
for(int j=w;j>=0;j--)
if(j+b[i]<=w)
f[j+b[i]]=max(f[j+b[i]],f[j]+t[i]);
else
f[w]=max(f[w],f[j]+t[i]);
}
// for(int i=w;i<=W;i++) if(f[i]>0) return 1;
return f[w]>=0.0;
}
可以发现这是一个树的结构,考虑树上背包,\(f_{i,j}\)表示在i的子树中选j个,会发现这个式子看起来像\(O(n^3)\)的,但其实是\(O(n^2)\)的,可以参考这篇博客 this
给你们看一下小代码
void dfs(int p) {
t[p]=a[p]-b[p]*mid;
int tot=0;
for(int i=1;i<=siz[p];i++) f[p][i]=-P;
for(auto i:v[p]) {
dfs(i);
for(int j=0;j<=tot;j++) {
for(int k=1;k<=siz[i];k++) {
f[p][j+k]=max(f[p][j+k],f[p][j]+f[i][k-1]+t[i]);
}
}
tot+=siz[i];
}
}

分数规划相关
浙公网安备 33010602011771号