集训队打比赛的时候挂在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; } /* 我是一只奔跑的小菜鸡…… */