HDU - 1864—最大报销额——算法笔记

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1864
Problem Description:

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

Input:

测试输入包含若干测试用例。每个测试用例的第1行包含两个正数 Q 和 N,其中 Q 是给定的报销额度,N(<=30)是发票张数。随后是 N 行输入,每行的格式为:
m Type_1:price_1 Type_2:price_2 … Type_m:price_m
其中正整数 m 是这张发票上所开物品的件数,Type_i 和 price_i 是第 i 项物品的种类和价值。物品种类用一个大写英文字母表示。当N为0时,全部输入结束,相应的结果不要输出。

Output:

对每个测试用例输出1行,即可以报销的最大数额,精确到小数点后2位。

Sample Input:

HDU - 1864—最大报销额——算法笔记_第1张图片

Sample Output:

在这里插入图片描述
解题思路:很明显,根据题意我们可以判断这题是 0 -1 背包类型问题。可以一看数据多了这么多,又不太像啊?没有问题,我们可以把数据整理整理,挑选出我们需要的数据,不要被题目的多种数据弄迷茫了。
  允许报销的发票类型包括买图书(A类)、文具(B类)、差旅(C类),要求每张发票的总额不得超过1000元,每张发票上,单项物品的价值不得超过600元
  题目中这句话很重要,这是整理数据的依据。先把满足的发票挑选出来,发票的额度存入 value[ ]数组存储值时有一个技巧,可以把这些钱都乘以100变成整数,这样就会方便后面的dp数组的动态遍历)。再
把可以报销的最大额度当做背包容量
,就可以得到我们想要的 0- 1背包问题模型了。
状态转移方程

 dp[j] = max(dp[j], dp[j - value[i]] + value[i]);
 dp[j]  表示 最大报销额度为 j 时,可以报销的最大值
 value[i]  表示 满足报销条件的发票的报销值

参考代码

#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
#include <stack>
#include <queue>
#include <map>
#include <set>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
using namespace std;

#define ll long long
#define clean(arrays) memset(arrays, 0, sizeof(arrays))

char a, b;
int N;
int num, sum, price;
float v, Q;
int value[40];
int dp[3000005];

int main()
{
    int i, j;
    while (cin>>Q>>N, N != 0)
    {
        clean(dp); clean(value);
        sum = (int)(Q*100);     //总报销额度变成整形
        int ans = 0;
        for (i = 1; i <= N; i++)    //遍历每张发票
        {
            int va = 0, vb = 0, vc = 0;     //价钱都变成整形
            cin>>num;
            bool flag = true;               //判断发票可不可以报销

            for (j = 1; j <= num; j++)      //遍历每张发票的物品
            {
                cin>>a>>b>>v;
                price = (int)(v*100);       //价钱都变成整形
                if (a == 'A')      va += price;
                else if (a == 'B') vb += price;
                else if (a == 'C') vc += price;
                else flag = false;          //出现其他类型商品就不能报销
            }

            if (flag && (va + vb + vc) <= 100000 && va <= 60000 && vb <= 60000 && vc <= 60000)  //挑选满足条件的发票
            {
                value[ans] = va + vb + vc;      //把发票总额度存入 value 数组
                ans++;
            }

        }

        // 0-1 背包模板
        for (i = 0; i <= ans; i++)
        {
            for (j = sum; j >= value[i]; j--)   //此处的遍历就体现了价格乘以 100 的好处
            {
                dp[j] = max(dp[j], dp[j - value[i]] + value[i]);
            }
        }
        printf("%.2f\n", dp[sum]/100.0);        //最后输出时不要忘记除以 100.0 返回原来数据类型
    }
    return 0;
}

你可能感兴趣的:(算法笔记)