POJ 3900 The Robbery(DFS+剪枝)

The Robbery
Time Limit: 5000MS   Memory Limit: 65536K
Total Submissions: 2216   Accepted: 274

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 (10 9), 
1 ≤ Wk, Ck ≤ 1000000000 (10 9).

参考了题解,学到了两点。
1、W,C值很大,数组开不下,所以不能用背包处理。但是N值很小,(1+15)*15/2=120,所以可以考虑dfs+剪枝。好的dfs+剪枝,可以媲美dp。
2.性价比。用贪心的思想对箱子进行排序,单位重量的价值最高的排第一。

#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;

const int maxN = 20;
struct NOTE
{
    long long weight;
    long long value;
    int num;
}box[maxN];
int n;
long long m,ans;
long long sum[maxN];  //保存后缀和

bool cmp(const struct NOTE &a, const struct NOTE &b)
{//按性价比排序,从大到小排列(注意若有取地址符号,则需有const)
    return a.value*1.0/a.weight > b.value*1.0/b.weight;
}
inline bool cut (int pos,long long now_value,long long last_weight)
{
    if(pos == n+1) return true;//边界返回条件
    if(now_value+sum[pos] < ans) return true;//如果后面所有的钻石加起来都<=ans,剪掉
    double best = (box[pos].value*1.0/box[pos].weight);//当前最大的性价比
    if(now_value+(long long)ceil(best*last_weight) < ans) return true;//如果剩下的所有重量<=ans,剪掉
    return false;
}
void dfs(int pos,long long now_value,long long last_weight) //pos 当前数组的下标位置,now_value 目前的重量和,last_weight当前背包剩余容量
{
    ans = MY_MAX(ans,now_value);
    if(cut(pos,now_value,last_weight))  return;  //剪枝
    for(int i=box[pos].num;i>=0;--i)   //(暴力搜索) 注意是从满到空枚举,利用ans剪枝
    {
        if(last_weight<box[pos].weight*i)   continue;
        dfs(pos+1,now_value+box[pos].value*i,last_weight-box[pos].weight*i);
    }
}

int main()
{
    int cas;
    long long sumv,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--)
        {
         //计算后缀和
            sum[i]=sum[i+1]+box[i].value*box[i].num;
        }
        dfs(1,0,m);
        printf("%lld\n",ans);
    }
    return 0;
}



猜你喜欢

转载自blog.csdn.net/daydream1000/article/details/78079813