01分数规划学习笔记
今天 gsh 带着我们复习了一下01分数规划。
01分数规划就是假设一个物体有两个属性 a,b,同时选择在这个集合中的k个物品,使:
[v=dfrac{sum^{i=1} _{i<=n} a_i cdot x_i}{sum ^{i=1} _{i<=n} b_i cdot x_i} ,sum_{i=1}^{n}x_i=k
]
的值最大。
那么如何能做到呢?看这个式子我们很难看出如何求解,那么将其变形。
[v cdot sum^{i=1} _{i<=n} b_i cdot x_i- sum^{i=1}_{i<=n} a_icdot x_i=0
]
提取公因式
[sum ^{i=1}_{i<=n} x_i cdot(vcdot b_i-a_i)=0
]
那么,我们可以二分一个v,设一个变量
[c_i=(vcdot b_i-a_i)
]
sort后选择最大的k个,二分如下。
while(r-l>1e-5) {
double mid=(l+r)/2;
if(check(mid))
l=mid;
else r=mid;
}
然后就有最优比率生成环(用spfa等等判正环),最优比率生成树(最大生成树)求出sumc是否大于0即可。
附一道题,poj2728,Desert King,最优比率生成树。
题目大意:有n个村庄,每个村庄有一个高度和坐标,要求建一个生成树,每条边有两个属性是距离与代价,要求单位长度的花费最小 (代价的大小就是这两个村庄的高度差的绝对值)。
//Writer : Hsz %WJMZBMR%tourist%hzwer
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#include<stack>
#include<vector>
#include<cstdlib>
#include<algorithm>
#define LL long long
using namespace std;
int n;
const int N=1004;
int x[N],y[N],z[N];
bool vis[N];
double a[N][N],b[N][N],c[N][N],dis[N];//邻接矩阵存图。
double dist(int d,int e) {
return (x[d]-x[e])*(x[d]-x[e])+(y[d]-y[e])*(y[d]-y[e]);
}
bool check(double v) {
memset(vis,0,sizeof vis);
vis[1]=1;
for(int i=1; i<=n; i++) dis[i]=b[1][i]-v*a[1][i];
double ans=0;
for(int i=2; i<=n; i++) {//prim求最小生成树
double tp=1061109567;
int k;
for(int j=2; j<=n; j++)
if(!vis[j] and dis[j]<tp) k=j,tp=dis[j];
if(k==-1) break;
vis[k]=1;
ans+=tp;
for(int j=2; j<=n; j++) {
if(!vis[j] and b[k][j]-v*(a[k][j])<dis[j]) {
dis[j]=b[k][j]-v*(a[k][j]);
}
}
}
return ans>=0;
}
int main() {
while(~scanf("%d",&n)&&n) {
for(int i=1; i<=n; i++) {
scanf("%d%d%d",&x[i],&y[i],&z[i]);
}
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++)
if(i!=j)
a[i][j]=sqrt(dist(i,j)),b[i][j]=abs(z[i]-z[j]);
double l=0,r=1e9;
while(r-l>1e-5) {//二分
double mid=(l+r)/2;
if(check(mid))
l=mid;
else r=mid;
}
printf("%.3lf
",l);
}
return 0;
}