集训队打比赛的时候挂在hdu上,这题没显示数据范围,然后全是用背包做,然后各种RE,MLE。等到比赛结束看到原题的数据范围后…………
赛后,教练给的解题报告是用dfs做的。
下面是自己的想法。
W,C值很大,数组开不下,但是发现N值很小,(1+15)*15/2=120,所以可以考虑dfs+剪枝。TLE一下午才憋出来。
首先利用贪心的思想我们对箱子进行排序,关键字为性价比(参考了poj里的discuss)。
也就是单位重量的价值最高的排第一,搜索的时候枚举顺序注意一定要从满到空,这样才能最快的找到一个可行解然后利用它进行接下来的剪枝。
剪枝1. 之后所有的钻石价值+目前已经得到的价值<=ans 则剪枝。
剪枝2. 剩下的重量全部装目前最高性价比的钻石+目前已经得到的价值<=ans 则剪枝。
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
struct node
{
long long value;
long long weight;
int num;
}box[20];
int n;
long long m;
long long ans;
long long sum[20]; //保存一个后缀和
bool cmp(const node &a,const node &b) //按性价比排序
{
// return a.value>b.value;
return a.value*1.0/a.weight>b.value*1.0/b.weight;
}
inline bool cut(int pos,long long value,long long left)
{
if(pos==n+1) return true; //边界剪枝
if(value+sum[pos]<=ans) return true; //如果后面所有的钻石加起来都<=ans,剪掉
double best=(box[pos].value*1.0/box[pos].weight); //当前最大的性价比
if(value+(long long)ceil(best*left)<=ans) return true; //以这个性价比取剩下的所有重量,如果<=ans,剪掉
return false;
}
void dfs(int pos,long long value,long long left)
{
ans=max(ans,value);
if(cut(pos,value,left)) return; //剪枝函数
for(int i=box[pos].num;i>=0;i--) //枚举顺序从满到空枚举,这样才能最快找到ans,然后利用ans剪枝
{
if(left-box[pos].weight*i<0) continue;
dfs(pos+1,value+box[pos].value*i,left-box[pos].weight*i);
}
}
int main()
{
int cas;
long long sumv;
long long sumw;
scanf("%d",&cas);
while(cas--)
{
ans=0;
sumv=sumw=0;
scanf("%d%lld",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%lld",&box[i].weight);
sumw+=box[i].weight*i;
}
for(int i=1;i<=n;i++)
{
scanf("%lld",&box[i].value);
box[i].num=i;
sumv+=box[i].value*i;
}
if(sumw<=m)
{
printf("%lld\n",sumv);
continue;
}
sort(box+1,box+1+n,cmp);
sum[n+1]=0;
for(int i=n;i>=1;i--)
{
// printf("value=%lld,weight=%d\n",box[i].value,box[i].weight);
sum[i]=sum[i+1]+box[i].value*box[i].num;
}
dfs(1,0,m);
printf("%lld\n",ans);
}
return 0;
}
/*
我是一只奔跑的小菜鸡……
*/