HDU 1864 最大报销额(01背包)

558 篇文章 0 订阅
273 篇文章 0 订阅

HDU 1864 最大报销额(01背包)

http://acm.hdu.edu.cn/showproblem.php?pid=1864

题意:

       现有一笔经费可以报销一定额度的发票。允许报销的发票类型包括买图书(A类)、文具(B类)、差旅(C类),要求每张发票的总额不得超过1000元,每张发票上,单项物品的价值不得超过600元。现请你编写程序,在给出的一堆发票中找出可以报销的、不超过给定额度的最大报销额。

分析:

       我们首先把合法的发票提取出来,那么剩下的工作就是在所有合法的发票中选出总额和尽量多的发票(且不超过最大报销额,即这部分工作就是01背包选发票的问题).

       如何找出合法的发票:

1. 发票中只能有A或B或C 这3类物品

2.一张发票的额度<=1000.

3. A BC三类物品的额度<=600.

通过上面规则过滤我们能找到所有合法的发票(及其对应的报销额),但是题目处理的数据都是浮点数,而01背包问题只能处理整数?

       题目给出的数据都是2位小数的且最后要求精确到小数点后2位,所以我们把所有数据都乘上100,然后让所有数据都强转成整数.(严格来说这里是有问题的,因为题目没说输入数据都只有2位小数.如果输入数据有3位及以上小数位,结果可能出错)

       这样处理后,我们就只需要考虑单纯的01背包问题了.

       假设有cnt个合法发票,每张发票的总额分别为val[i],在所有发票额度总和不超过max_val的基础上,最多能报销多少?

       令dp[i][j]=x表示处理完(选或不选)第i张发票后,总额不超过j时能报销的最大额度为x.

       dp[i][j]=max( dp[i-1][j]dp[i-1][j-val[i]]+val[i])

       程序实现用的是一维滚动数组.

AC代码: 265ms

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=1000*30*100+5;//30张1000元的发票(扩大100倍)

int max_val;  //可报销总额
int dp[maxn];

int cnt;      //合法发票数
int val[maxn];//每张合法发票的额度

int main()
{
    double v;
    int n;
    while(scanf("%lf%d",&v,&n)==2 && n)
    {
        cnt=0;
        max_val=(int)(v*100);
        for(int i=1;i<=n;i++)
        {
            int num;
            char type;//商品类型
            double va=0,vb=0,vc=0;
            scanf("%d",&num);
            bool flag=true;//合法发票
            while(num--)
            {
                scanf(" %c:%lf",&type,&v);

                if(type=='A') va+=v;
                else if(type=='B') vb+=v;
                else if(type=='C') vc+=v;
                else flag=false;//含有其他种类商品
            }

            if(flag && va<=600 && vb<=600 && vc<=600 && va+vb+vc<=1000)
                val[++cnt]=(int)((va+vb+vc)*100);
        }

        memset(dp,0,sizeof(dp));

        for(int i=1;i<=cnt;i++)
            for(int j=max_val;j>=val[i];j--)
                dp[j] = max(dp[j],dp[j-val[i]]+val[i]);

        printf("%.2lf\n",(dp[max_val])/100.0 );
    }
    return 0;
}

AC代码2: 利用<<背包9>>中优化下界后, 109ms

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=1000*30*100+5;//30张1000元的发票(扩大100倍)

int max_val;  //可报销总额
int dp[maxn];

int cnt;      //合法发票数
int val[maxn];//每张合法发票的额度

int main()
{
    double v;
    int n;
    while(scanf("%lf%d",&v,&n)==2 && n)
    {
        cnt=0;
        max_val=(int)(v*100);
        for(int i=1;i<=n;i++)
        {
            int num;
            char type;//商品类型
            double va=0,vb=0,vc=0;
            scanf("%d",&num);
            bool flag=true;//合法发票
            while(num--)
            {
                scanf(" %c:%lf",&type,&v);

                if(type=='A') va+=v;
                else if(type=='B') vb+=v;
                else if(type=='C') vc+=v;
                else flag=false;//含有其他种类商品
            }

            if(flag && va<=600 && vb<=600 && vc<=600 && va+vb+vc<=1000)
                val[++cnt]=(int)((va+vb+vc)*100);
        }

        memset(dp,0,sizeof(dp));
        int sum[100];//sum[i]为val[i]+val[i+1]+...val[cnt]
        sum[cnt+1]=0;
        for(int i=cnt;i>=1;i--)
            sum[i]=sum[i+1]+val[i];

        for(int i=1;i<=cnt;i++)
        {
            int bound=max(max_val-sum[i+1],val[i]);//下界
            for(int j=max_val;j>=bound;j--)
                dp[j] = max(dp[j],dp[j-val[i]]+val[i]);
        }


        printf("%.2lf\n",(dp[max_val])/100.0 );
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值