目录
0-1背包
点菜问题
http://t.cn/AiYOrkXr
已知背包的容量(capacity)和每个物品的体积(volume)、重量(weight),求背包能够容纳的物体最大重量。
记忆化搜索:
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;
int main() {
int c,n;
while(cin>>c>>n){
vector<int> money(n);
vector<int> score(n);
for(int i=0;i<n;i++) {
cin>>money[i];
cin>>score[i];
}
vector<vector<int>> cache(n,vector<int>(c+1,-1));
function<int(int,int)> dfs=[&](int i,int c)->int{
if(i<0) return 0;
if(cache[i][c]!=-1) return cache[i][c];
if(c<money[i]) return cache[i][c]=dfs(i-1,c);
return cache[i][c]=max(dfs(i-1,c),dfs(i-1,c-money[i])+score[i]);
};
int res=dfs(n-1,c);
cout<<res<<endl;
}
}
递推:
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;
int main() {
int c, n;
while (cin >> c >> n) {
vector<int> money(n);
vector<int> score(n);
for (int i = 0; i < n; i++) {
cin >> money[i];
cin >> score[i];
}
vector<int> dp(c + 1, 0);
for (int i = 0; i <n ; i++) {
for (int j = c; j >= money[i]; j--) {
dp[j] = max(dp[j], dp[j - money[i]] + score[i]);
}
}
int res = dp[c];
cout << res << endl;
}
}
LeetCode494 目标和
准备两个背包,一个背包package_a
存放标记为正的元素,另一个背包package_b
存放标记为负的元素。
package_a - package_b = target
package_a + package_b = sum(sum为
nums的元素和)
则 package_a = (target + sum)/2
如果target + sum<0或(target + sum)%2!=0,无解。
转化为以下问题:
已知背包的容量(capacity=package_a)和每个物品的体积(volume),求恰好装满背包的物体组合数量。
记忆化搜索:
class Solution {
public:
int findTargetSumWays(vector<int>& nums, int target) {
int sum=0;
int n=nums.size();
for(int i=0;i<n;i++) sum+=nums[i];
if((sum+target)%2!=0||(sum+target)<0) return 0;
int c=(sum+target)/2;
vector<vector<int>> cache(n,vector<int>(c+1,-1));
function<int(int,int)> dfs=[&](int i,int c)->int{
if(i<0) return c==0;
if(cache[i][c]!=-1) return cache[i][c];
if(c<nums[i]) return cache[i][c]=dfs(i-1,c);
return cache[i][c]=dfs(i-1,c)+dfs(i-1,c-nums[i]);
};
return dfs(n-1,c);
}
};
LeetCode2787 将一个数字表示成幂的和的方案数
class Solution {
public:
int numberOfWays(int n, int x) {
int mod=1e9+7;
vector<int> dp(n+1,0);
dp[0]=1;
for(int i=1;pow(i,x)<=n;i++){
for(int j=n;j>=pow(i,x);j--){
dp[j]=(dp[j]+dp[j-pow(i,x)])%mod;
}
}
return dp[n];
}
};
LeetCode416 分割等和子集
已知背包的容量(capacity=sum/2)和每个物品的体积(volume),求是否存在恰好装满背包的物体组合。
记忆化搜索:
class Solution {
public:
bool canPartition(vector<int>& nums) {
int sum=0;
int n=nums.size();
for(int i=0;i<n;i++) sum+=nums[i];
if(sum%2!=0) return false;
int c=sum/2;
vector<vector<long long>> cache(n,vector<long long>(c+1,-1));
function<bool(int,int)> dfs=[&](int i,int c)->bool{
if(i<0) return c==0;
if(cache[i][c]!=-1) return cache[i][c];
if(c<nums[i]) return cache[i][c]=dfs(i-1,c);
return cache[i][c]=dfs(i-1,c)||dfs(i-1,c-nums[i]);
};
return dfs(n-1,c);
}
};
最小邮票数
已知背包的容量(capacity)和每个物品的体积(volume),在所有能够恰好装满背包的物体组合当中,求最少的物体数量。
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;
int fun(int M, vector<int>& nums,int N) {
int Max = 0x3f3f3f3f;
vector<int> dp(M+1,Max);
dp[0]=0;
for(int i=0;i<N;i++){
for(int j=M;j>=nums[i];j--){
dp[j]=min(dp[j],dp[j-nums[i]]+1);
}
}
if(dp[M]==Max) return 0;
return dp[M];
}
int main() {
int M;
while (cin >> M) {
int N;
cin>>N;
vector<int> nums(N);
for (int i = 0; i < N; i++) cin >> nums[i];
int res=fun(M,nums,N);
cout << res << endl;
}
}
LeetCode2915 和为目标值的最长子序列的长度
已知背包的容量(capacity)和每个物品的体积(volume),在所有能够恰好装满背包的物体组合当中,求最多的物体数量。
class Solution {
public:
int lengthOfLongestSubsequence(vector<int>& nums, int target) {
int n=nums.size();
int Min=-0x3f3f3f3f;
vector<int> dp(target+1,Min);
dp[0]=0;
for(int i=0;i<n;i++){
for(int j=target;j>=nums[i];j--){
dp[j]=max(dp[j],dp[j-nums[i]]+1);
}
}
return dp[target]>0?dp[target]:-1;
}
};