01分数规划

更好的阅读体验

01 分数规划

什么是 01 分数规划

用人话说,就是:

n n n 个玩意儿,每个都有两个属性 ( x , y ) (x,y) (x,y)。现在要从中选出几个玩意儿,使得 ∑ x ∑ y \frac{\sum x}{\sum y} yx 最大

但是有些人仍然不懂。没关系,可以用数学语言表示:

有三个序列 x , y , z x,y,z x,y,z 长度为 n n n

z z z 满足 ∀ i ∈ [ 1 , n ] , z i ∈ ( 1 , 0 ) \forall i\in [1,n],z_i\in(1,0) i[1,n],zi(1,0)

然后得到一种合法的 z z z 的取值,最大化:
∑ i = 1 n x i × z i ∑ i = 1 n y i × z i \frac{\sum\limits_{i=1}^{n} x_i\times z_i}{\sum\limits_{i=1}^{n} y_i\times z_i} i=1nyi×zii=1nxi×zi

怎么解这个问题

解法:二分法。

首先先转移:

L = ∑ i = 1 n x i × z i ∑ i = 1 n y i × z i L=\frac{\sum\limits_{i=1}^{n} x_i\times z_i}{\sum\limits_{i=1}^{n} y_i\times z_i} L=i=1nyi×zii=1nxi×zi, 则
L × ∑ i = 1 n y i × z i = ∑ i = 1 n x i × z i ⇓ ∑ i = 1 n x i × z i − L × ∑ i = 1 n y i × z i = 0 ⇓ ∑ i = 1 n ( x i − y i × L ) × z i = 0 L\times \sum\limits_{i=1}^{n} y_i\times z_i=\sum\limits_{i=1}^{n} x_i\times z_i \\ \Downarrow\\ \sum\limits_{i=1}^{n} x_i\times z_i-L\times \sum\limits_{i=1}^{n} y_i\times z_i=0 \\ \Downarrow\\ \sum\limits_{i=1}^{n} (x_i-y_i\times L)\times z_i=0 L×i=1nyi×zi=i=1nxi×zii=1nxi×ziL×i=1nyi×zi=0i=1n(xiyi×L)×zi=0
相当于拥有了一个数组 a , a i = x i − y i × L a,a_i=x_i-y_i\times L a,ai=xiyi×L,从 a a a中选一些数使得总和最大。

显然,选择所有的正数即可。

这个 L L L 就是二分中的 m i d mid mid 了。

由于这个 L L L 是二分得到的,所以每一次不一定都是
∑ i = 1 n ( x i − y i × L ) × z i = 0 \sum\limits_{i=1}^{n} (x_i-y_i\times L)\times z_i=0 i=1n(xiyi×L)×zi=0
而是
∑ i = 1 n ( x i − y i × L ) × z i ≥ 0 \sum\limits_{i=1}^{n} (x_i-y_i\times L)\times z_i\geq0 i=1n(xiyi×L)×zi0
也就是在代码的二分中判断此刻的 m i d mid mid 可不可行时,在 if 里面写上这个。

板题

这个不算很板的题目,贴一下吧。
P4377 [USACO18OPEN] Talent Show G
这里附一份代码:
code

#include<bits/stdc++.h>
using namespace std;

#define int long long

const int MAXN=1e5+5;
const double eps=1e-6;

int n,W;
int w[MAXN],t[MAXN],maxz;
double a[MAXN];
double f[MAXN];

signed main()
{
    scanf("%lld%lld",&n,&W);
    for(int i=1;i<=n;i++)
        scanf("%lld%lld",&w[i],&t[i]),maxz+=t[i];
    double l=0,r=maxz;
    while(r-l>eps)
    {
        double mid=(l+r)/2;
        for(int i=1;i<=n;i++)
            a[i]=t[i]-w[i]*mid;
        for(int i=1;i<=W;i++) f[i]=-1e9;
        for(int i=0;i<=n;i++)
        {
            for(int j=W;j>=0;j--)
                f[min(j+w[i],W)]=max(f[min(j+w[i],W)],f[j]+a[i]);
        }
        if(f[W]>=eps)
            l=mid;
        else
            r=mid-eps;
    }
    printf("%lld\n",(int)floor(1000*l));
    return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值