動態規劃 0-1揹包問題及優化

動態規劃解0-1揹包問題

過程:

依次計算數組中的每一個值:當容量C超過了當前物品的重量時,說明當前物品可以放入揹包中,那麼就要考慮是否將該物品放入揹包中,如果放入揹包中,當前揹包的總價值就爲memo[i-1][j-w[i]]+v[i],如果不放入揹包中,當前揹包的總價值就爲memo[i-1][j],使用這兩種情況的最大值更新數組數據

代碼:

#include <iostream>
#include <vector>
using namespace std;
/*
[1,2,3]
[6,10,12]
解:22 
*/
//動態規劃解 
class Solution {
public:
    int beibao(vector<int>& w,vector<int>& v, int C) {
		int m = w.size();
		if(m==0) return 0;
		vector<vector<int> > memo(m,vector<int>(C+1,0));//存儲第[0,i]件物品在容量C下能獲得的最大總價值 
		for(int j=0; j<=C; j++)
			memo[0][j]=(j>=w[0]?v[0]:0);
			
		for(int i=1; i<m; i++)
		{
			for(int j=0; j<=C; j++)
			{
				memo[i][j] = memo[i-1][j];
				if(j>=w[i]) //當前物品有機會裝到揹包中
				{
					//不選擇該物品和選擇該物品 
					memo[i][j] = max(memo[i-1][j],memo[i-1][j-w[i]]+v[i]); 
				} 
			}
		} 
		return memo[m-1][C]; 
    }
};
int main()
{
	vector<int> w;
	vector<int> v;
	w.push_back(1);
	w.push_back(2);
	w.push_back(3);
	v.push_back(6);
	v.push_back(10);
	v.push_back(12);
	cout<<Solution().beibao(w,v,5)<<endl;
	return 0;
}

時間複雜度:O(n*C)

空間複雜度:O(n*C)

優化1

兩行空間輪流去使用

#include <iostream>
#include <vector>
using namespace std;
//動態規劃解 優化1 
class Solution {
public:
    int beibao(vector<int>& w,vector<int>& v, int C) {
		int m = w.size();
		if(m==0) return 0;
		vector<vector<int> > memo(2,vector<int>(C+1,0));//存儲第[0,i]件物品在容量C下能獲得的最大總價值 
		for(int j=0; j<=C; j++)
			memo[0][j]=(j>=w[0]?v[0]:0);
		
		//後一行的計算,只依賴於前一行,偶數行用第一行,奇數行用第二行	
		for(int i=1; i<m; i++)
		{
			for(int j=0; j<=C; j++)
			{
				memo[i%2][j] = memo[(i-1)%2][j];
				if(j>=w[i]) //當前物品有機會裝到揹包中
				{
					//不選擇該物品和選擇該物品 
					memo[i%2][j] = max(memo[(i-1)%2][j],memo[(i-1)%2][j-w[i]]+v[i]); 
				} 
			}
		} 
		return memo[(m-1)%2][C]; 
    }
};
int main()
{
	vector<int> w;
	vector<int> v;
	w.push_back(1);
	w.push_back(2);
	w.push_back(3);
	v.push_back(6);
	v.push_back(10);
	v.push_back(12);
	cout<<Solution().beibao(w,v,5)<<endl;
	return 0;
}

優化2

是否可以只使用一行大小爲C的數組完成動態規劃:從右向左刷新格子內容

對於第2個物品,當容量爲5的時候,memo[j] = max(memo[j],memo[j-w[i]]+v[i]);  

代碼

#include <iostream>
#include <vector>
using namespace std;

//動態規劃解 優化2 
class Solution {
public:
    int beibao(vector<int>& w,vector<int>& v, int C) {
		int m = w.size();
		if(m==0) return 0;
		vector<int> memo(C+1,0);//存儲第[0,i]件物品在容量C下能獲得的最大總價值 
		for(int j=0; j<=C; j++)
			memo[j]=(j>=w[0]?v[0]:0);
		
		//從右向左更新數據	
		for(int i=1; i<m; i++)
		{
			for(int j=C; j>=w[i]; j--)
			{
				//當前物品有機會裝到揹包中
				//不選擇該物品和選擇該物品 
				memo[j] = max(memo[j],memo[j-w[i]]+v[i]);  
			}
		} 
		return memo[C]; 
    }
};
int main()
{
	vector<int> w;
	vector<int> v;
	w.push_back(1);
	w.push_back(2);
	w.push_back(3);
	v.push_back(6);
	v.push_back(10);
	v.push_back(12);
	cout<<Solution().beibao(w,v,5)<<endl;
	return 0;
}

面試中的0-1揹包問題 LeetCode 416

題目

416. 分割等和子集
給定一個只包含正整數的非空數組。
是否可以將這個數組分割成兩個子集,使得兩個子集的元素和相等。
注意:
每個數組中的元素不會超過 100
數組的大小不會超過 200
示例 1:
輸入: [1, 5, 11, 5]
輸出: true
解釋: 數組可以分割成 [1, 5, 5] 和 [11].
 
示例 2:
輸入: [1, 2, 3, 5]
輸出: false
解釋: 數組不能分割成兩個元素和相等的子集.

思路

代碼

#include <iostream>
#include <vector>
using namespace std;
//動態規劃解
//可以看做一個典型的動態規劃問題,從數組中選取n個數,讓他們構成sum/2 
class Solution {
public:
    bool canPartition(vector<int>& nums) {
    	int n=nums.size();
    	int sum=0;
		for(int i=0; i<n; i++)
			sum+=nums[i];
		
		if(sum%2!=0) return false; //如果不是偶數,則肯定分割不了 
		sum = sum/2; 
		vector<vector<bool> > memo(n,vector<bool>(sum+1,false));//代表考慮[0,i]中的元素,是否可以組成j
		for(int i=0; i<=sum; i++)
			if(i==nums[0]) memo[0][i] = true;//只考慮第0個元素,是否可以組成sum
		
		for(int i=1; i<n; i++)
		{
			for(int j=0; j<=sum; j++)
			{
				memo[i][j] = memo[i-1][j];
				//對於當前的第i個數,可以是組成當前j的數,也可以不是 
				if(j>=nums[i])
					memo[i][j] = memo[i-1][j] || memo[i-1][j-nums[i]];
			}
			
		}
		
		return memo[n-1][sum]; 
    }
};
int main()
{
	
	return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章