ACM杂题——动态规划_背包问题

ACM杂题K - I NEED A OFFER!——动态规划_背包问题优化解法

题目描述

Speakless很早就想出国,现在他已经考完了所有需要的考试,准备了所有要准备的材料,于是,便需要去申请学校了。要申请国外的任何大学,你都要交纳一定的申请费用,这可是很惊人的。Speakless没有多少钱,总共只攒了n万美元。他将在m个学校中选择若干的(当然要在他的经济承受范围内)。每个学校都有不同的申请费用a(万美元),并且Speakless估计了他得到这个学校offer的可能性b。不同学校之间是否得到offer不会互相影响。“I NEED A OFFER”,他大叫一声。帮帮这个可怜的人吧,帮助他计算一下,他可以收到至少一份offer的最大概率。(如果Speakless选择了多个学校,得到任意一个学校的offer都可以)。

Input

输入有若干组数据,每组数据的第一行有两个正整数n,m(0<=n<=10000,0<=m<=10000)
后面的m行,每行都有两个数据ai(整型),bi(实型)分别表示第i个学校的申请费用和可能拿到offer的概率。
输入的最后有两个0。

Output

每组数据都对应一个输出,表示Speakless可能得到至少一份offer的最大概率。用百分数表示,精确到小数点后一位。

Sample Input

10 3
4 0.1
4 0.2
5 0.3
0 0

Sample Output

44.0%

Hint

You should use printf("%%") to print a '%'.

题目大意

有n万元,够付某些学校的申请费,每个学校的通过率为Pi,先要明确的是至少有一个学校通过的概率   P=1-\sum \left ( 1 - P[i] \right )  ,最后在m所学校中要找出够付的情况下的

最大值P。

化解成背包问题描述,m个学校为m件商品,每个学校的申请费a为商品质量(或体积),每个学校不录取率为商品的价值,手中有的总钱数n为书包的容量。现在需要在商品

能装进书包的情况下,让装入书包的所有商品的价值的乘积最小。1-P就是最大的被至少一个学校录取的概率。

 

题解

常规的背包问题的解法设设置一个二维数组a,高位i为商品个数,低位j为书包容积数,储存的是对于第i件商品,书包容积为j时可以储存的商品价值成绩的最小值(常规题目思想是商品价值和的最大值)。运用两个for循环来填充数组a,外层循环为商品个数,内层循环书包容积数,判断条件为当前商品的体积money[i]<=容积j,检查更新公式:a[i][j]=min(a[i-1][j],a[i-1][j-money[i]]*realp[i])  (含义为不装入当前商品i时和装入商品i时哪个价值总乘积最小,把最小的赋给当前值)。否则体积大于容积就等于不装入该商品的最小值a[i-1][j].

这个方法的问题在于当商品数很多、书包容积很大时数组开得太大会爆掉内存,而且当当前商品体积小于容积是没有操作的,浪费大量了的时间和空间。因此将数组化简成一维,只存放对于当前体积j的最小的商品价值乘积的最小值,还是用两个for循环,外层商品数,内层体积数来遍历,内循环控制为最大容积到当前商品的体积,这样避免判断商品不能装入书包的情况(无法装入自然不会更新,所以可以直接不判断),检查更新数组a的公式为  a[j]=min(a[j],a[j-money[i]]*realp[i])  。同时记录更新最小值,最后for循环遍历完之后输出(1-最小值)即可。

ps:本篇文章暂时只讨论了0-1背包问题(同类商品只有一个)

代码

#include<iostream>
#include<iomanip>
using namespace std;
struct offer
{
    int mon;
    double realp;  //是用1-p得到的,真正用于计算的“商品的价值”
    double p;
};
double mymin(double x,double y)
{
    if(x<y)
        return x;
    else
        return y;
}
int main()
{
    int n=0,m=0;
    cin>>n>>m;
    while(n!=0 || m!=0)
    {
        offer *x=new offer[m+1];
        double *maxx=new double [n+1];
        for(int i=1;i<=m;i++)
        {
            cin>>x[i].mon>>x[i].p;
            x[i].realp=1-x[i].p;
        }
        for(int i=0;i<=n;i++)
            maxx[i]=1.0;
        double big=1.0;
        for(int i=1;i<=m;i++)
        {
            for(int j=n;j>=x[i].mon;j--)
            {
                maxx[j]=mymin(maxx[j],maxx[j-x[i].mon]*x[i].realp);//j容量将能装入的
                if(maxx[j]<big)
                    big=maxx[j];
            }
        }
        cout<<setiosflags(ios::fixed)<<setprecision(1)<<(1-big)*100<<"%"<<endl;
        cin>>n>>m;
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值