题目描述
牛牛准备参加学校组织的春游, 出发前牛牛准备往背包里装入一些零食, 牛牛的背包容量为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; }
原文地址:https://www.cnblogs.com/xidian-mao/p/11368388.html
时间: 2024-10-27 06:58:29