- 背包最大承受能力为w,总商品个数为n,每件商品的重量各不相同,如何能让背包内的商品总重量达到最大值?(回溯背包)
代码:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
void dfs(vector<int> &vec,int w,int curw,int i,int n,int &res) {
//如果物品个数达到最大值,或者当前总重量达到能承受最大值,保存并终止
if(n==i || w>=curw)
{
res = max(res,curw);
return;
}
//装第i个商品(如果装上后超过了最大承受力,忽略)
if(curw+vec[i] <= w)
dfs(vec,w,curw+vec[i],i+1,n,res);
//不装第i个商品
dfs(vec,w,curw,i+1,n,res);
}
int main()
{
int w,n;
cout<<"最大承受力"<<endl;
cin>>w;
cout<<"输入商品个数"<<endl;
cin>>n;
vector<int> vec;
int weight;
for(int i=0;i<n;i++)
{
cin>>weight;
vec.push_back(weight);
}
int res = INT_MIN;
dfs(vec,w,0,0,n,res);
cout<<res<<endl;
return 0;
}
- leetcode-416-分割等和子集
题型:回溯、动态规划、0-1背包
难度:中等
题目:给定一个只包含正整数的非空数组。是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
代码:(回溯背包法)
class Solution {
public:
bool dfs(vector<int>& nums,int n,int i,int last){
if(last == 0) return true;
if(i >= n) return false;
if(nums[i] == last) return true;
if(nums[i] > last)//如果当前这个数比需要的数还大,不要直接false,越过它,往后看
{
return dfs(nums,n,i+1,last);
}
else//满足的话,选不选都看,有一个方案满足就可以
{
return dfs(nums,n,i+1,last-nums[i])||dfs(nums,n,i+1,last);
}
}
bool canPartition(vector<int>& nums) {
int n = nums.size();
if(n < 2) return false;
int sum = 0;
for(int i=0;i<n;i++)
sum += nums[i];
//总和是奇数,false
if(sum % 2 == 1) return false;
//从大到小排序
sort(nums.rbegin(),nums.rend());
sum = sum/2;
if(nums[0] > sum) return false;
if(nums[0] == sum) return true;
return dfs(nums,n,0,sum);
}
};
代码(动态规划):
class Solution {
public:
bool canPartition(vector<int>& nums) {
int n = nums.size();
if(n < 2) return false;
int sum = 0;
for(int i=0;i<n;i++)
sum += nums[i];
if(sum % 2 == 1) return false;
sum /= 2;
vector<vector<bool> > dp(n,vector<bool>(sum+1,false));
//边界值
if(nums[0] <= sum)
dp[0][nums[0]] = true;
for(int i=1;i<n;i++)
{
for(int j=0;j<=sum;j++)
{
if(nums[i] == j)
dp[i][j] = true;
else if(nums[i] > j)
dp[i][j] = dp[i-1][j];
else
dp[i][j] = dp[i-1][j] || dp[i-1][j-nums[i]];
}
}
return dp[n-1][sum];
}
};
- leetcode-1049-最后一块石头的重量2
题型:中等
题目:有一堆石头,每块石头的重量都是正整数。每一回合,从中选出任意两块石头,然后将它们一起粉碎。假设石头的重量分别为 x 和 y,且 x <= y。那么粉碎的可能结果如下:
如果 x == y,那么两块石头都会被完全粉碎;
如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x。
最后,最多只会剩下一块石头。返回此石头最小的可能重量。如果没有石头剩下,就返回 0。
代码:
class Solution {
public:
int lastStoneWeightII(vector<int>& stones) {
int n = stones.size();
if(n < 2) return 0;
int sum = 0;
for(int i=0;i<n;i++)
sum += stones[i];
int all = sum;
sum = sum/2;
vector<vector<int> > dp(n+1,vector<int>(sum+1,0));
for(int i=1;i<=n;i++)
{
int cur = stones[i-1];
for(int j=1;j<=sum;j++)
{
dp[i][j] = max(dp[i][j],dp[i-1][j] );
if( j >= cur)
dp[i][j] = max(dp[i][j],dp[i-1][j-cur]+cur );
}
}
return all-2*dp[n][sum];
}
};
- leetcode-494-目标和
题型:01背包
难度:中等
题目:给定一个非负整数数组,a1, a2, …, an, 和一个目标数,S。现在你有两个符号 + 和 -。对于数组中的任意一个整数,你都可以从 + 或 -中选择一个符号添加在前面。
返回可以使最终数组和为目标数 S 的所有添加符号的方法数。
代码:
//如何看出是01背包呢?在满足条件的一种方法里,所有元素里面,加号一组减号一组,假设加号组和为a,减号组和为b,a-b=S,a+b=Sum,转化为--->2*a=S+sum--->a=(S+sum)/2;那么如果S+sum是偶数,可以推断出,从所有元素中取元素放入背包,让其最大价值为(S+sum)/2, 在这种情况下,得到的方案数。
class Solution {
//dp[i][j]表示用数组内前i个元素,组成和为1的方案数
public:
int findTargetSumWays(vector<int>& nums, int S) {
int n = nums.size();
if(n == 0 || S>1000) return 0;
vector<vector<int> > dp(n,vector<int>(2005,0));
dp[0][nums[0]+1000] = 1;
dp[0][1000-nums[0]] += 1;
for(int i=1;i<n;i++)
{
for(int j=-1000;j<=1000;j++)
{
if(dp[i-1][j+1000] > 0)
{
dp[i][j+1000+nums[i]] += dp[i-1][j+1000];
dp[i][j+1000-nums[i]] += dp[i-1][j+1000];
}
}
}
return dp[n-1][S+1000];
}
};