poj 3900 The Robbery(dfs+剪枝,不是背包)

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


你可能感兴趣的:(poj 3900 The Robbery(dfs+剪枝,不是背包))