牛牛的揹包問題(另類揹包問題)

🔗:node

題目描述

牛牛準備參加學校組織的春遊, 出發前牛牛準備往揹包裏裝入一些零食, 牛牛的揹包容量爲w。
牛牛家裏一共有n袋零食, 第i袋零食體積爲v[i]。
牛牛想知道在整體積不超過揹包容量的狀況下,他一共有多少種零食放法(整體積爲0也算一種放法)。

輸入描述:

輸入包括兩行
第一行爲兩個正整數n和w(1 <= n <= 30, 1 <= w <= 2 * 10^9),表示零食的數量和揹包的容量。
第二行n個正整數v[i](0 <= v[i] <= 10^9),表示每袋零食的體積。

輸出描述:

輸出一個正整數, 表示牛牛一共有多少種零食放法。
示例1

輸入

複製
3 10
1 2 4

輸出

複製
8

說明

三種零食整體積小於10,因而每種零食有放入和不放入兩種狀況,一共有2*2*2 = 8種狀況。

核💗: 動態規劃的核心就是合併狀態,使搜索空間變小. 這個問題因爲揹包太大的緣故, 使用揹包複雜度O(nm);很明顯不行, 網上對這道題的作法是dfs, 說白了就是暴力枚舉,
當這樣的數據, n=30, m=29, n個糖果質量爲1時, 運算次數會達到2^30次...
總之題目數據不強, 怎麼辦呢...
我是這樣作的, 另類二分, 將數據非爲兩半, 分別暴力枚舉倆個部分(2^15), 而後把兩部分狀態相加小於揹包容量的方案加起來, 這樣就好啦~~
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
struct node {
    LL val;
    LL cnt;
};
int n; LL v;
LL w[35];
vector<node> f(int l, int r) {
    vector<node> ans;
    int len=r-l+1;
    int base=1<<len;
    map <LL, int> mapp;
    for (int i=0;i<base;i++) {
        LL tsum=0;
        for (int k=0;k<len;k++)
            if (i&(1<<k))
                tsum+=w[k+l];
        mapp[tsum]++;
    }
    for (auto it: mapp) {
        node tmp={it.first, it.second};
        ans.push_back(tmp);
    }
    return ans;
}
int main()
{
    scanf("%d %lld",&n,&v);
    for (int i=1;i<=n;i++)
        scanf("%lld",&w[i]);
    vector<node> a1=f(1, n/2);
    vector<node> a2=f(n/2+1, n);
    for (int i=1;i<a2.size();i++)
        a2[i].cnt+=a2[i-1].cnt;
    int l1=a1.size();
    int j=a2.size()-1;
    LL ans=0;
    for (int i=0;i<l1;i++) {
        while (j>=0&&a1[i].val+a2[j].val>v) j--;
        if (j<0) break;
        ans+=a1[i].cnt*a2[j].cnt;
    }
    printf("%lld\n",ans);
    return 0;
}
相關文章
相關標籤/搜索