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; }