POJ 3900 The Robbery (dfs暴搜+剪枝)

来源:互联网 发布:linux xargs cat命令 编辑:程序博客网 时间:2024/06/01 21:17
The Robbery
Time Limit: 5000MS Memory Limit: 65536KTotal Submissions: 1543 Accepted: 151

Description

In the downtown of Bucharest there is a very big bank with a very big vault. Inside the vault there are N very big boxes numbered from 1 to N. Inside the box with number k there are k very big diamonds, each of weight Wk and cost Ck.
John and Brus are inside the vault at the moment. They would like to steal everything, but unfortunately they are able to carry diamonds with the total weight not exceeding M.
Your task is to help John and Brus to choose diamonds with the total weight less than or
equal to M and the maximal possible total cost.

Input

The first line contains single integer T – the number of test cases. Each test case starts with a line containing two integers N and M separated by a single space. The next line contains N integers Wk separated by single spaces. The following line contains N integers Ck separated by single spaces.

Output

For each test case print a single line containing the maximal possible total cost of diamonds.

Sample Input

2 2 4 3 2 5 3 3 100 4 7 1 5 9 2

Sample Output

6 29

Hint

Constraints:
1 ≤ T ≤ 74,
1 ≤ N ≤ 15,
1 ≤ M ≤ 1000000000 (109),
1 ≤ Wk, Ck ≤ 1000000000 (109).

Source

Southeastern European Regional Programming Contest 2009

题意:

有n种物品,第 i 种有 i 个。每种物品的价值和重量相同。求小偷偷走总重量不超过m的物品的最大价值。

分析:

由于Wk和Ck的数量级都达到了10^9,数组存不下。而n很小,所以考虑dfs暴搜加上剪枝。

参考《acm背包问题九讲》中的P11背包问题的搜索解法。

其中

  

搜索的剪枝

基本的剪枝方法不外乎可行性剪枝或最优性剪枝。

可行性剪枝即判断按照当前的搜索路径搜下去能否找到一个可行解,例如:若将剩下所有物品都放入背包仍然无法将背包充满(设题目要求必须将背包充满),则剪枝。

最优性剪枝即判断按照当前的搜索路径搜下去能否找到一个最优解,例如:若加上剩下所有物品的权值也无法得到比当前得到的最优解更优的解,则剪枝。

搜索的顺序

在搜索中,可以认为顺序靠前的物品会被优先考虑。所以利用贪心的思想,将更有可能出现在结果中的物品的顺序提前,可以较快地得出贪心地较优解,更有利于最优性剪枝。所以,可以考虑将按照性价比(权值/费用)来排列搜索顺序。

另一方面,若将费用较大的物品排列在前面,可以较快地填满背包,有利于可行性剪枝。

最后一种可以考虑的方案是:在开始搜索前将输入文件中给定的物品的顺序随机打乱。这样可以避免命题人故意设置的陷阱。

以上三种决定搜索顺序的方法很难说哪种更好,事实上每种方法都有适用的题目和数据,也有可能将它们在某种程度上混合使用。

故这题剪枝可包括:

1、先按性价比递减的顺序排序,即单位重量价值最大的排在最前面;

2、剪枝1:当前所选物品的总价值+所有剩下物品的价值 <= ans (前一次搜索时的最优解) ,则剪枝。

3、剪枝2:剩余重量全部装此时性价比最高的物品,即当前所搜pos的物品。装入后,其价值+sum<=ans,则剪枝。

搜索的顺序也要注意:搜索的时候枚举顺序注意一定要从满到空,这样更有利于最优性剪枝。

那个递归需要好好理解:每次搜索都把每个袋子里选或不选,选多少个的情况分别搜索了。ans的比较也是在这些情况中找的最优解。

代码:

#include<cstdio>#include<algorithm>#include<iostream>#define maxx(a,b) (a)>(b)?(a):(b)typedef __int64 ll;   //用typedef很方便using namespace std;struct node{    ll w;    ll v;    double vbw;  //性价比    int id;}djw[20];ll left_sumv[20];   //剩下物品的总价值ll ans,sum;int n,m;bool cmp(node a,node b)    //按性价比降序排列{    return a.vbw>b.vbw;}void dfs(int pos,ll sum,ll left)  //pos=当前搜到的位置 sum=当前的总价值 left=当前剩余重量{    ans=maxx(ans,sum);if(pos>n) return;   //越界    if(sum + left_sumv[pos-1] <= ans) return;   //剪枝1    if(sum + left*djw[pos].vbw <= ans) return;  //剪枝2    for(int k=djw[pos].id;k >= 0;k--)      //注意搜索顺序    {        if(left - k*djw[pos].w < 0) continue;        dfs(pos+1,sum+k*djw[pos].v,left-k*djw[pos].w);    }}int main(){    int t,i;    scanf("%d",&t);    while(t--)    {ans=0;    //ans初始化为0        scanf("%d%d",&n,&m);        for(i=1;i<=n;i++)        {            scanf("%I64d",&djw[i].w);            djw[i].id=i;        }        for(i=1;i<=n;i++){            scanf("%I64d",&djw[i].v);djw[i].vbw=1.0*djw[i].v/djw[i].w;}/*for(i=1;i<=n;i++){printf("tt : %I64d %I64d\n",djw[i].w,djw[i].v);}*/sort(djw+1,djw+n+1,cmp);//for(i=1;i<=n;i++)    //printf("%d\n",djw[i].id);        left_sumv[n]=0;        for(i=n;i>=1;i--)    //求后缀和,即不包括自己在内的剩余物品的总价值            left_sumv[i-1]=left_sumv[i]+djw[i].id*djw[i].v;    dfs(1,0,m);printf("%I64d\n",ans);    }return 0;}


 

over~
原创粉丝点击