背包问题


#pragma once
#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

class Solution_back
{
	//vector<int> vs{ 0,2,4,3,7 };
	//vector<int> ws{ 0,2,3,5,5 };
	vector<int> vs{-1,-1,1,6};
	vector<int> ws{-1,1,-1,6};
	int P[4]{0,2,3,4}; 
	int V[4]{0,3,4,5};
	int M[4]{0,4,3,2};

public:
	void test_ks()
	{
		//01背包测试
		//cout << ks1(4,10) << endl;
		//cout << ks2(4, 10) << endl;
		//cout << ks3(4, 10) << endl;
		//cout << ks4(4, 10) << endl;
		////完全背包测试
		//cout << ks5(4, 10) << endl;
		////多重背包测试
		//cout << ks6(3, 15) << endl;
		//cout << ks7(3, 15) << endl;
		cout << ks8(4, 4) << endl;
	}

	//01背包 --- 递归解法
	int ks1(int n, int m)
	{
		int result = 0;
		// 递归结束条件
		if (n == 0 || m == 0) return result;
		else if (m < ws[n]) //如果装不下
			result = ks1(n - 1 ,m);
		else
			result = max(ks1(n - 1, m), ks1(n - 1, m - ws[n]) + vs[n]);
		return result;
	}

	//01背包 --- 动态规划:自上而下填表法
	int ks2(int n, int m)
	{
		//初始化操作
		vector<vector<int>> dp(n + 1,vector<int>(m + 1 , 0));
		for (int i = 1; i <= n; ++i)
		{
			for (int j = 1; j <= m; ++j)
			{
				if (j < ws[i]) dp[i][j] = dp[i - 1][j];
				else
					dp[i][j] = max(dp[i - 1][j] , dp[i - 1][j - ws[i]] + vs[i]);
			}
		}

		return dp[n][m];
	}

	//01背包 --- 动态规划:自底向上的填表方式
	// 滚动数组 --- 将二维压缩到一维
	//形式1
	int ks3(int n, int m)
	{
		//初始化操作
		vector<int> dp(m + 1 , 0);
		for (int i = 1; i <= n; ++i)
		{
			for (int j = m; j >= ws[i]; --j)//这里逆序遍历j,以到达更优就覆盖的过程,循环结束条件是当放不进去的时候就结束内层循环
				dp[j] = max(dp[j] , dp[j - ws[i]] + vs[i]);
		}

		return dp[m];
	}

	//滚动数组 --- 形式2
	int ks4(int n, int m)
	{
		//声明一个dp[2][m + 1]的只有两行的二维数组
		vector<vector<int>> dp(2, vector<int>(m + 1, 0));
		for (int i = 1; i <= n; ++i)
			for (int j = 1; j <= m; ++j)
				if (j < ws[i])
					dp[i % 2][j] = dp[(i - 1) % 2][j];
				else
					dp[i % 2][j] = max(dp[(i - 1) % 2][j], dp[(i - 1) % 2][j - ws[i]] + vs[i]);

		return max(dp[0][m],dp[1][m]);
	}

	//完全背包问题
	//物品的数量是无限次可以拿取的,我们在01背包的基础进行修改。正好利用01背包的空间优化算法,这时候
	//我们不逆序执行状态转移方程,而是正序执行,正好可以实现每种武平的重复利用,即相当于每种物品有无限个

	int ks5(int n, int m)
	{
		vector<int> dp(m + 1, 0);
		for (int i = 1; i <= n; ++i)
			for (int j = ws[i]; j <= m; ++j)
				dp[j] = max(dp[j] , dp[j - ws[i]] + vs[i]);
		return dp[m];
	}

	int ks51(int n, int m)
	{
		vector<int> dp(m + 1, 0);
		for (int i = 1; i <= n; ++i)
			for (int j = ws[i]; j <= m; ++j)
				dp[j] = max(dp[j], dp[j - ws[i]] + vs[i]);
		return dp[m];
	}

	//多重背包问题
	//介于01背包和完全背包之间的算法问题,物品的数量有限次,增加一个物品数量数组M,物品价值数组P,物品重量数组V
	//递推式: dp[i][j] = max{dp[i - 1][j - V[i]*k] + P[i]*k}; (0 <= k  <= M[i]  && 0 <= k * V[i] <= j);
	//这个递推式相对于完全背包而言,就是多了一个限制条件:0 <= k  <= M[i] ;其他都是一样的;

	// 多重背包问题 --- 简单递归
	int ks6(int n, int m)
	{
		int result = 0;
		if (n == 0 || m == 0) return result;
		else if (m < V[n]) result = ks6(n - 1, m);
		else
		{
			for (int k = 0; k <= M[n] && k * V[n] <= m; ++k)
			{
				int temp = ks6(n - 1, m - V[n] * k) + P[n] * k;
				if (result < temp)
					result = temp;
			}
		}

		return result;
	}

	//多重背包问题 --- 动态规划空间优化算法
	//note:这里要将补的0删除掉
	int ks7(int i , int t)
	{
		vector<int> newResults(t + 1);
		// 开始填表
		for (int m = 0; m <= i; m++) 
		{
			// 考虑第m个物品
			// 分两种情况
			// 1: M[m] * V[m] > T 则可以当做完全背包问题来处理
			if (M[m] * V[m] >= t) {
				for (int n = V[m]; n <= t; n++) {
					newResults[n] = max(newResults[n], newResults[n - V[m]] + P[m]);
				}
			}
			else {
				// 2: M[m] * V[m] < T 则需要在 newResults[n-V[m]*k] + P[m] * k 中找到最大值,0 <= k <= M[m]
				for (int n = V[m]; n <= t; n++) {
					int k = 1;
					while (k < M[m] && n > V[m] * k) {
						newResults[n] = max(newResults[n], newResults[n - V[m] * k] + P[m] * k);
						k++;
					}
				}
			}
		}

		return newResults[t];
	}



	//滚动数组 --- 形式2
	int ks8(int n, int m)
	{
		//声明一个dp[2][m + 1]的只有两行的二维数组
		vector<vector<int>> dp(2, vector<int>(5101, 0));
		for (int i = 0; i < n; ++i)
		{
			for (int j = 0; j <= m; ++j)
			{
				if (ws[i] < 0)
				{
					dp[i % 2][j - ws[i]] = max(dp[(i - 1) % 2][j], dp[(i - 1) % 2][j - ws[i]] + vs[i]);
					m -= ws[i];
				}
				if (j < ws[i])
					dp[i % 2][j] = dp[(i - 1) % 2][j];
				else
					dp[i % 2][j] = max(dp[(i - 1) % 2][j], dp[(i - 1) % 2][j - ws[i]] + vs[i]);
			}
		}

				

		return max(dp[0][m], dp[1][m]);
	}

};






全部评论

相关推荐

程序员花海:实习和校招简历正确格式应该是教育背景+实习+项目经历+个人评价 其中项目经历注意要体现业务 实习经历里面的业务更是要自圆其说 简历模板尽可能保持干净整洁 不要太花哨的
985本硕1个中小厂of...
点赞 评论 收藏
分享
10-29 19:42
门头沟学院 Java
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务