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