01分数规划

题目一般是这样的:有N个物品,每个物品重c[i],价值v[i],从中选K个,求

v[i]use[i]c[i]use[i] ∑ v [ i ] ∗ u s e [ i ] ∑ c [ i ] ∗ u s e [ i ]
最大
其中use[i]是0 或 1,表示选或不选这件物品

设最终答案为 res r e s
即:

res=v[i]use[i]c[i]use[i] r e s = ∑ v [ i ] ∗ u s e [ i ] ∑ c [ i ] ∗ u s e [ i ]

化简一哈:
(resc[i]v[i])use[i])=0 ∑ ( r e s ∗ c [ i ] − v [ i ] ) ∗ u s e [ i ] ) = 0

但是我们不知道res是多少,所以就设为 x 好啦~
设一大坨为 f(x) f ( x )
即:
f(x)=(resc[i]v[i])use[i]) f ( x ) = ∑ ( r e s ∗ c [ i ] − v [ i ] ) ∗ u s e [ i ] )
x x 不一样,f(x)就不一样,并且当 x x 越接近最终的 res f(x) f ( x ) 就越接近0对吧

于是我们就来猜 x x ,运气好的话,刚好猜到一个x使得 f(x)=0 f ( x ) = 0
But
However
Nevertheless
Whereas
Yet
我是一个喜欢靠实力的大帅比~而不喜欢靠运气0.0
所以我们发现 f(x) f ( x ) 是与 x x 有关的,
我的这个f(x)是增函数, x x 增大,f(x)就增大
所以,
当我们得到 f(x)>0 f ( x ) > 0 时,那就想要把 x x 减小
反之,f(x)<0时,那就要把 x x 弄大
所以,就是二分嘛

#include"iostream"
#include"algorithm"
#include"iomanip"
using namespace std;
const int maxn=1e5+5;
const double eps=1e-6;
int c[maxn],v[maxn];
struct AAA
{
    double f;
    int id;/*这个id是用来打印出来到底是选了哪K个*/
    bool operator<(const AAA &a)const
    {
        return f<a.f;
    }
};
AAA use[maxn];
int main()
{
    cout.setf(ios::fixed);
    int T;
    cin>>T;
    int N,K;
    while(T--)
    {
        cin>>N>>K;
        for(int i=0;i<N;i++)cin>>c[i]>>v[i];
        double L=-100,R=100;/*这里的边界要取得合适,上界要大于答案,下界要小于答案*/
        double x,fx;
        while(R-L>eps)
        {
            x=(L+R)/2.0;
            for(int i=0;i<N;i++)
            {
                use[i].f=1.0*x*c[i]-1.0*v[i];
                use[i].id=i+1;
            }
            sort(use,use+N);
            fx=0;
            for(int i=0;i<K;i++)fx+=use[i].f;/*因为要最大的嘛,就选出最大的K个*/
            if(fx>0)R=x;
            else L=x;
        }
        cout<<setprecision(2)<<x<<endl;

    }
}

第二种方法:

迭代法:

如果我们选的那K个物品的 average=v[i]c[i] 就是最终的res的话,那肯定是等于我们用这K个得到的average 来算的 f(x) f ( x )

这计算的与上一次计算的会越来越近,当他们的差小于一定值时,就认为相等,就是答案了


//迭代
#include"iostream"
#include"algorithm"
#include"iomanip"
using namespace std;
const int maxn=1e5+5;
const double eps=1e-6;
int c[maxn],v[maxn];
struct AAA
{
    double c,v,f;
    bool operator<(const AAA &a)const
    {
        return f<a.f;
    }
};
AAA use[maxn];
int main()
{
    cout.setf(ios::fixed);
    int T;
    cin>>T;
    int N,K;
    while(T--)
    {
        cin>>N>>K;
        for(int i=0;i<N;i++)cin>>use[i].c>>use[i].v;
        /*先随便猜一个答案*/
        double res=rand();
        double res_calc=res+1;/*这样是为了进循环*/
        while(fabs(res_calc-res)>eps)
        {
            res=res_calc;
            for(int i=0;i<N;i++)use[i].f=res*use[i].c-use[i].v;
            sort(use,use+N);
            double c_use=0,v_use=0;
            for(int i=0;i<K;i++)
            {
                c_use+=use[i].c;
                v_use+=use[i].v;
            }
            res_calc=1.0*v_use/c_use;/*res_calc就是根据res选的这K个计算而来的*/
        }
        cout<<setprecision(2)<<res<<endl;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值