题目地址: http://ac.jobdu.com/problem.php?pid=1025
题目描述:
现有一笔经费可以报销一定额度的发票。允许报销的发票类型包括买图书(A类)、文具(B类)、差旅(C类),要求每张发票的总额不得超过1000元,每张发票上,单项物品的价值不得超过600元。现请你编写程序,在给出的一堆发票中找出可以报销的、不超过给定额度的最大报销额。
输入:
测试输入包含若干测试用例。每个测试用例的第1行包含两个正数 Q 和 N,其中 Q 是给定的报销额度,N(N<=30)是发票张数。随后是 N 行输入,每行的格式为:
m Type_1:price_1 Type_2:price_2 ... Type_m:price_m
其中正整数 m 是这张发票上所开物品的件数,Type_i 和 price_i 是第 i 项物品的种类和价值。物品种类用一个大写英文字母表示。当N为0时,全部输入结束,相应的结果不要输出。
输出:
对每个测试用例输出1行,即可以报销的最大数额,精确到小数点后2位。
样例输入:
200.00 3
2 A:23.50 B:100.00
1 C:650.00
3 A:59.99 A:120.00 X:10.00
1200.00 2
2 B:600.00 A:400.00
1 C:200.50
1200.50 3
2 B:600.00 A:400.00
1 C:200.50
1 A:100.00
100.00 0
样例输出:
123.50
1000.00
1200.50
来源:
2007年浙江大学计算机及软件工程研究生机试真题
答疑:
解题遇到问题?分享解题心得?讨论本题请访问:http://t.jobdu.com/thread-7749-1-1.html
【解题思路】
其实是01背包,但是在处理基础数据的时候比较复杂。
有以下几种限制。
1、发票必须是A、B、C中的其中一种或者多种,如果出现D、E等等,这张发票就作废了。
2、每张发票上可能开多种类别,但是每种类别钱数不能超过600,总和不得超过1000。
这样处理完毕以后,就知道有多少张发票可以使用。
接下来,问题就简化为有一堆待报销的发票,最多可以报销q,求在限制范围内,可以报销的最大额是多少。
发票要么报销,要么不报销,典型的01背包。
但是,还得注意,因为有类型的限制,有可能会出错。
不妨将double乘倍数取整,这样处理起来方便又不容易出错,这是在很多题目中所使用的技巧之一。
Java AC
import java.util.ArrayList; import java.util.List; import java.util.Scanner; import java.util.regex.Pattern; public class Main { /* * 1025 */ public static void main(String[] args) throws Exception { Scanner scanner = new Scanner(System.in); while (scanner.hasNext()) { double q = scanner.nextDouble(); int n = scanner.nextInt(); if (n == 0) { break; } List<Integer> numList = new ArrayList<Integer>(); while (n > 0) { double A = 0; double B = 0; double C = 0; int valid = 0; int m = scanner.nextInt(); while (m > 0) { String a = scanner.next(); String typeArr[] = a.split(Pattern.quote(":")); double price = Double.parseDouble(typeArr[1]); if (typeArr[0].equals("A")) { A += price; }else if (typeArr[0].equals("B")) { B += price; }else if (typeArr[0].equals("C")) { C += price; }else { valid = 1; } m--; } if (valid == 0 && A <= 600.00 && B <= 600.00 && C <= 600.00) { double total = A + B + C; if (total <= 1000.00 && total <= q) { numList.add((int) (total*100)); } } n--; } int len = numList.size(); int hunq = (int) (q * 100); int dp[] = new int[hunq + 1]; for (int i = 0; i < len; i++) { int tempNum = numList.get(i); for (int j = hunq; j >= tempNum; j--) { dp[j] = Math.max(dp[j], dp[j - tempNum] + tempNum ); } } double res = (double)(dp[hunq]/100.00); System.out.printf("%.2f\n" , res); } } } /************************************************************** Problem: 1025 User: wangzhenqing Language: Java Result: Accepted Time:420 ms Memory:49096 kb ****************************************************************/
C++ AC
#include <stdio.h> const int maxn = 32; int n,i; double q; int numArr[32]; int max(int a, int b){ return a > b ? a : b; } int main(){ while(scanf("%lf %d",&q,&n) != EOF){ if(n == 0){ break; } int len = 0; while (n > 0) { double A = 0; double B = 0; double C = 0; double price; int valid = 0; char c; int m; scanf("%d",&m); while (m > 0) { getchar(); scanf("%c:%lf", &c, &price); if (c == 'A') { A += price; }else if (c == 'B') { B += price; }else if (c == 'C') { C += price; }else { valid = 1; } m--; } if (valid == 0 && A <= 600.00 && B <= 600.00 && C <= 600.00) { double total = A + B + C; if (total <= 1000.00 && total <= q) { numArr[len] = (int)(total*100); len++; } } n--; } int hunq = (int) (q * 100); int *dp = new int[hunq + 1]; for(i = 0; i < hunq+1; i++){ dp[i] = 0; } for (i = 0; i < len; i++) { int tempNum = numArr[i]; for (int j = hunq; j >= tempNum; j--) { dp[j] = max(dp[j], dp[j - tempNum] + tempNum ); } } double res = (double)(dp[hunq]/100.00); printf("%.2lf\n",res); } return 0; } /************************************************************** Problem: 1025 User: wangzhenqing Language: C++ Result: Accepted Time:30 ms Memory:21816 kb ****************************************************************/