题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1864
解题思路:首先这题一看就是01背包,那么要思考的就是用什么作为背包容量,这里报销价格是浮点数,所以我们最好选择发票张数来作为背包的容量。这样数组也非常小,因为最多才30张
那么状态转移方程就是 dp[j] = max (dp[j],dp[j-1]+price); 那么很明显报销张数越多报销金额越大,发票张数从大到小循环,直到遇到满足条件的报销金额,那么它就是答案。
这题有些坑,首先每张发票上,同类商品价值和不能超过600,还有除A、B、C 三类商品外,含有其他品种该发票也作废。我是先用gechar() 处理掉字符,再接受浮点数。
AC代码:
#include <stdio.h> #include <string.h> #include <algorithm> #define MAX 33 using namespace std; double dp[MAX]; int main () { int n; char ch1; double p = 0;//允许报销的最大金额 double price = 0; double p_a = 0;//分别对应a.b.c三类物品的总价值 double p_b = 0; double p_c = 0; while(scanf("%lf%d",&p,&n)== 2 && n) { int m = 0 ; memset (dp,0,sizeof(dp)); for (int i=0;i<n;++i) { scanf("%d",&m); price = 0; int flag = 1;//标记该发票是否具有报销资格。1表示有 p_a = p_b =p_c = 0; for (int j = 0;j<m;++j) { double t_p; int p_ch = 0; while( ( ch1 = getchar() ) !=':')//对字符的处理 { if(ch1 =='A') p_ch = 1; if (ch1 == 'B') p_ch = 2; if (ch1 == 'C') p_ch = 3; } scanf ("%lf",&t_p); if (p_ch == 0) flag = 0;//作废的票 if (p_ch == 1) p_a +=t_p; if (p_ch == 2) p_b +=t_p; if (p_ch == 3) p_c +=t_p; if (p_a > 600 || p_b > 600 || p_c >600) flag = 0; price += t_p; //记录整张发票的总额 } if (price > 1000 || !flag) continue; for (int j = n;j>= 1;--j) dp[j] = max (dp[j],dp[j-1]+price); } double ans = 0; for (int i =n;i>=0;--i) if (dp[i] <= p) {ans = dp[i];break;} printf ("%.2lf ",ans); } return 0 ; }