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;
}

猜你喜欢

转载自www.cnblogs.com/sdfzhsz/p/9094398.html