Description
一个劫匪带着一个可装m重量的超大背包去抢银行,银行有n个大箱子,第i个箱子里有i个重量为wi,价值为ci的大钻石,问该劫匪抢走钻石的最大价值
Input
第一行为一整数T表示用例组数,每组用例第一行为两个整数n和m分别表示钻石个数和背包可装钻石重量上限,第二行为n个整数wi表示第i箱子中每颗钻石的重量,第三行为n个整数ci表示第i箱子中每颗钻石的价值
(1<=T<=74,1<=n<=15,1<=wi,ci,m<=1e9)
Output
对于每组用例,输出该劫匪带走钻石的最大价值
Sample Input
2
2 4
3 2
5 3
3 100
4 7 1
5 9 2
Sample Output
6
29
Solution
此题咋一看以为是01背包,看到背包容量以为是超大背包,但物品个数太多,所以只能用dfs了,而搜索中需要注意几点,首先是先将所有钻石的性价比降序排,每次优先选择性价比高的钻石,而且选择这种钻石的数量也要从高到低枚举搜索,同时有两步重要剪枝:
设当前已经选到第pos种钻石,背包剩余容量为left,已经选取的钻石重量为sum,最优解为ans
剪枝一:
sum+left*c[pos]/w[pos] < ans,说明就算将背包剩余容量全部放当前这种性价比最高的钻石也没有已经达到的最优解大的话停止搜索
剪枝二:
sum+sum(c[pos]*cnt[pos]+c[pos+1]*cnt[pos+1]+…+c[n]*cnt[n]) < ans,说明就算将剩余钻石全部拿上也没有已经达到的最优解大的话停止搜索
剪枝三:
枚举当前钻石数量i时,left < w[pos]*i,说明背包容量已经不够,无需继续搜索,继续下一次枚举即可
Code
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
int t,n,m;
ll ans,d_sum[22];
struct node
{
ll w,c;//钻石重量以及价值
double wc;//钻石性价比
int cnt;//钻石数量
}d[22];
int cmp(node a,node b)//将钻石按性价比降序排
{
return a.wc>b.wc;
}
void dfs(int pos,ll sum,ll left)
{
ans=max(ans,sum);//更新最优解
if(pos>=n||left==0)return ;//搜索完毕
if(sum+left*d[pos].wc<=ans)return ;//剪枝一
if(sum+d_sum[pos]<=ans)return ;//剪枝二
for(int i=d[pos].cnt;i>=0;i--)//注意枚举次序,数量由高到低枚举
{
if(left-d[pos].w*i<0)continue;//剪枝三
dfs(pos+1,sum+d[pos].c*i,left-d[pos].w*i);//深搜
}
}
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++)scanf("%lld",&d[i].w),d[i].cnt=i+1;
for(int i=0;i<n;i++)scanf("%lld",&d[i].c),d[i].wc=1.0*d[i].c/d[i].w;
d_sum[n]=0;
for(int i=n-1;i>=0;i--)//统计后缀钻石总价值
d_sum[i]=d_sum[i+1]+d[i].c*d[i].cnt;
sort(d,d+n,cmp);//排序
ans=0;
dfs(0,0,m);
printf("%lld\n",ans);
}
return 0;
}