01分数规划问题 - 笔记

1、01分数规划

详解:https://blog.csdn.net/hzoi_ztx/article/details/54898323
在这里插入图片描述
一般会固定 选择的二元数组的个数(当固定的个数等于顶点数减一时,也就是最小/大比率生成树的问题了 ),例如:power oj 2881

2、为什么不能贪心?

(后面两个问题都理解了好久 ༼༎ຶᴗ༎ຶ༽
或许可以借助一个数学问题来理解:两堆不同价格的商品混合,怎么定价才能不赚不赔?

定价= 两 堆 商 品 的 总 质 量 两 堆 商 品 的 总 价 格 \frac {两堆商品的总质量} {两堆商品的总价格} ,而且单价较低的商品占的比例越大,定价就会越低。

再回到01分数规划的问题中,比如在下面的三个二元数组中选两个二元数组,可以发现选1、3两组比选1、2两组的结果更优。所以并不能单根据比值大小来选取(占比也会影响最终结果)。

3 1 、 80 40 ( 2 × 40 1 × 40 ) 、 1.9 1 \frac {3} {1} 、\frac {80} {40}(\frac {2×40} {1×40})、\frac {1.9} {1} 134080(1×402×40)11.9

3、为什么可以二分解01分数规划?

感觉博主结合图形的解释很容理解了,这里写一下自己的理解过程:我们要求 选取二元数组个数为 k k k 时, r r r 的最大值(下面的求和均只包含 x i = 1 x_i=1 xi=1的部分)

r = ∑ v a l u e i ∑ c o s t i r=\frac {\sum value_i} {\sum cost_i} r=costivaluei

∑ v a l u e i − r ∗ ∑ c o s t i = 0 {\sum value_i -r* \sum cost_i=0} valueircosti=0

我们先做出所有组合对应的 f ( x ) = ∑ v a l u e i − x ∗ ∑ c o s t i f(x)={\sum value_i -x* \sum cost_i} f(x)=valueixcosti的图像,每个图像的横截距(零点)就对这个组合对应的 r r r 的值,这些图像最大的横截距也就是 r r r 的最大值 r ∗ r^* r
在这里插入图片描述
在不知道 r ∗ r^∗ r的情况下:

在图中上任取一条垂直 x x x 轴的竖线,
如果存在直线与这条竖线的交点纵坐标为正,那么最优值一定在当前竖线的右侧;
如果所有直线与这条竖线交点纵坐标为负,那么最优值一定在当前竖线的左侧;
如果所有直线与这条竖线交点纵坐标非正且存在直线与这条竖线交点纵坐标为0,那么当前竖线横坐标即为最优值 r ∗ r^∗ r

如上图,要判断存在性或一定性,其实只需判断最大的 f ( 7 ) f(7) f(7) 是否大于0,而最大的 f ( 7 ) f(7) f(7) 就是所有二元数组对应的 v a l u e i − 7 ∗ c o s t i { value_i -7*cost_i} valuei7costi 中前 k k k 大的和(任意 k k k 个的组合,肯定是前 k k k 大加起来结果最大)。

另外通过这个式子 v a l u e i − x ∗ c o s t i { value_i -x*cost_i} valueixcosti 或者图像也可以看出: x x x 的值越大前 k k k 大的和越小,所以可以用二分求解。

power oj 2881:Luojie学姐想要代码少出bug,所以决定去女装,商店里有n条小裙子每条小裙子的价格和漂亮程度分别为wi和pi,luojie学姐想要k条小裙子,ta希望你能帮ta选出k条小裙子,使得单位价格的漂亮程度最大。

#include<algorithm>
#include<iostream>
#include<stdio.h>
#include<vector>
#include<string>
#include<cstring>
#include<math.h>
using namespace std;
//#define int long long
const int manx=1e4+10;
const int manx2=1e7+10;
const int mod=1e9+7;
const double inf=1e9+7;

int n,k;
double p[manx],w[manx];
int check(double x)
{
    double a[manx],ans=0;
    for(int i=1;i<=n;i++)
        a[i]=w[i]-x*p[i];
    sort(a+1,a+n+1,[](int x1,int x2){return x1>x2;});
    for(int i=1;i<=k;i++)
        ans+=a[i];
    return ans>=0;
}
int main()
{
    //ios::sync_with_stdio(false);
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)
        scanf("%lf%lf",&p[i],&w[i]);
    double l=0,r=1e6;
    while(r-l>1e-4)//这里写成1e-3还wa了一发
    {
        double mid=(l+r)/2;
        if(check(mid))
            l=mid;
        else r=mid;
    }
    printf("%.2f\n",l);
    return 0;
}

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值