01分数规划学习笔记
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_i\cdot x_i=0
\]
提取公因式
\[\sum ^{i=1}_{i<=n} x_i \cdot(v\cdot b_i-a_i)=0
\]
那么,我们可以二分一个v,设一个变量
\[c_i=(v\cdot 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\n",l);
}
return 0;
}
我是咸鱼。转载博客请征得博主同意Orz