LightOJ1127 - Funny Knapsack(折半搜索+二分)

版权声明:Amove? https://blog.csdn.net/Amovement/article/details/88723644

                                                                           1127 - Funny Knapsack

    PDF (English) Statistics Forum
Time Limit: 2 second(s) Memory Limit: 32 MB

Given n integers and a knapsack of weight W, you have to count the number of combinations for which you can add the items in the knapsack without overflowing the weight.

Input

Input starts with an integer T (≤ 100), denoting the number of test cases.

Each case contains two integers n (1 ≤ n ≤ 30) and W (1 ≤ W ≤ 2 * 109) and the next line will contain n integers separated by spaces. The integers will be non negative and less than 109.

Output

For each set of input, print the case number and the number of possible combinations.

Sample Input

Output for Sample Input

3

1 1

1

1 1

2

3 10

1 2 4

Case 1: 2

Case 2: 1

Case 3: 8

 


PROBLEM SETTER: JANE ALAM JAN

一、原题地址

点我传送

二、大致题意

给定n个(1<=n<=30)整数和一个重量为W (1 ≤ W ≤ 2 * 10^9) 的背包,你必须计算你可以在背包中添加物品而不会超出重量的组合数量。

三、大致思路

看到物品的数量是30,背包是10^9规模,DP好像没有什么好的解决办法。那么就想着怎么暴力。

首先如果暴力地枚举每种物品是否塞入背包中的物品就会有2^30种方案,显然这是超时的。那么为了降低这个复杂度我们把物品平均分成两堆,这样再来枚举的话每一堆最多有的方案就只有2^15个了,2^15大概1e6不到,这是我们可以跑出来的数据。

我们用ansa来储存第一堆物品跑出来的重量,ansb保存第二堆物品跑出来的重量。

那么对于ansb来说,如果ansa中存在一个数据ansa[ i ]+ansb[ j ]<=w,那么这就是一个可行方案了。这里n^2的复杂度又不能接受了,所以采取sort ansa,那么我们只需要二分一下最大的w-ansb[ j ]所在的位置p,对于一个 j 可行的方案数就加上p。


四、代码

#include <iostream>
#include <cstring>
#include <cmath>
#include <cstdio>
#include <algorithm>
#include <vector>
#include<set>
#include<map>
#include<queue>
using namespace std;
const double eps = 1e-6;
typedef long long LL;
const int inf=0x3f3f3f3f;
const LL INF=0x3f3f3f3f3f3f3f3f;


int T,n;
LL a[20],b[20],w;
bool vis[(1<<16)];
vector<LL>ansa,ansb;

void read()
{
    scanf("%d %lld",&n,&w);
    for(int i=1;i<=n/2+1;i++)scanf("%lld",&a[i]);
    for(int i=1;i<=n-(n/2+1);i++)scanf("%lld",&b[i]);
}

void DFS(vector<LL>&ans,LL aa[],int Size,LL sum,int state)
{
    ans.push_back(sum);
    vis[state]=true;
    for(int i=1;i<=Size;i++)
    {
        int nex=state|(1<<(i-1));
        if(nex==state)continue;
        if(sum+aa[i]<=w&&!vis[nex])
        {
            DFS(ans,aa,Size,sum+aa[i],nex);
        }
    }
    return ;
}

int main()
{
    scanf("%d",&T);
    int Ca=1;
    while(T--)
    {
        read();
        ansa.clear();ansb.clear();
        for(int i=0;i<(1<<(n/2+1));i++)vis[i]=false;
        DFS(ansa,a,n/2+1,0,0);
        //跑出第一堆的可行方案时的重量
        for(int i=0;i<(1<<(n-n/2-1));i++)vis[i]=false;
        DFS(ansb,b,n-n/2-1,0,0);
        //跑出第二堆的可行方案时的重量

        LL ans=0;
        sort(ansa.begin(),ansa.end());
        for(int i=0;i<ansb.size();i++)
        {
            ans+=(LL)(upper_bound(ansa.begin(),ansa.end(),w-ansb[i])-ansa.begin());
        }
        //二分可行位置
        printf("Case %d: %lld\n",Ca++,ans);

    }
}

猜你喜欢

转载自blog.csdn.net/Amovement/article/details/88723644