0-1背包问题(算法进化历程)

/*
	Name: 0-1背包问题(算法进化历程) 
	Copyright: 
	Author: 
	Date: 24-07-17 21:10
	Description: 7113_Charm Bracelet
查看 提交 统计 提问
总时间限制: 1000ms 内存限制: 65536kB
描述
Bessie has gone to the mall's jewelry store and spies a charm bracelet. Of course, 
she'd like to fill it with the best charms possible from the N(1 ≤N≤3,402) available charms.
 Each charm iin the supplied list has a weight Wi(1 ≤Wi≤400), a 'desirability' factor Pi(1 ≤Pi≤100), 
 and can be used at most once. Bessie can only support a charm bracelet whose weight is no more than M(1 ≤M≤12,880).
Given that weight limit as a constraint and a list of the charms with their weights and desirability rating, 
deduce the maximum possible sum of ratings.
输入
Line 1: Two space-separated integers: N and M
Lines 2..N+1: Line i+1 describes charm i with two space-separated integers: Wi and Di
输出
Line 1: A single integer that is the greatest sum of charm desirabilities that can be achieved given the weight constraints

题意:经典的0-1背包问题,有N(1 ≤N≤3402)个物品,编号为i的物品的重量为Wi(1 ≤Wi≤400),价值为Pi(1 ≤Pi≤100),
现在要从这些物品中选择一些物品装到一个容量为C(1 ≤C≤12880) 的背包中,使得背包内物品在总重量不超过M的前提下价值尽量大。
输入
第1行:分别代表物品数量和背包容量的两个整数N和C
第2..N+1行:每行两个整数Wi和Di,分别表示第i个物品的重量和价值
输出 
一个整数,表示最大的总价值。
 
样例输入
4 6
1 4
2 6
3 12
2 7
样例输出
23

1.题目分析:
考虑到每种物品只有2种选择,即装入背包或不装入背包,并且物品数和背包容量已给定,
要计算装入背包物品的最大价值和最优装入方案,可用动态规划算法思想。

2.算法设计:
物品有n种,背包容量为C,分别用p[i]和w[i]存储第i种物品的价值和重量,用
x[i]标记第i种物品是否装入背包,用数组p[n][c]记录给定n个物品装入容量为c的背包的最大价值。 

动态规划有两种基本思路,一种是自顶而下的备忘录算法,它采用递归的方式,一步步缩小问题的规模,
找到边界(n==0)以后,处理好边界(bestP = (c >= W[n]) ? P[n] : 0;),然后一步步返回,返回更大问题的解。
每获得一个子问题的答案,便保存到备忘录数组p[n][c],避免下次充分计算,提升了效率。
另一种思路是自底向上的动态规划算法,它从最小的子问题(只包含0号物品)开始处理,然后依次增加可装物品的数量,
用数组p[n][c]记录每一个已处理子问题的答案,由于0-1背包问题具有最优子结构,
每个问题的最优解都是由其子问题的最优解组成,故这样依次递增问题的规模,可以得到最终需要的最优解。 
 
3. 复杂度分析:
无论是自顶向下的备忘录算法还是自底向上的动态规划算法,都可以在O(n^3)时间内求解。
他们有O(n^2)个备忘记录项(或子问题),这些记录项的初始化耗费O(n^2)时间。
每个记录项只填入一次,每次填入时,耗费O(n)时间。因而填入O(n^2)各记录项总共耗费O(n^3)时间。
对每个子问题,两种方法都只解一次,并记录答案,再碰到该子问题时,不重新求解而是取用已有答案。
它们节省了计算量,提高了算法的效率。自底向上的动态规划算法按顺序计算了每个子问题的解;
自顶向下的备忘录算法采用递归的方式只计算了确实需要求解的子问题,效率更高(当然,由于需要调用递归函数,
有了一些额外的开销),也更便于理解。 

改进的动态规划算法:
算法3:使用动态规划算法时,注意到 B[i][j] = max(B[i-1][j], B[i-1][j-W[i]]+P[i]);故可以用两个一维数组代替二维数组。 
算法4:在算法3的基础上进一步优化,注意到总是由下标较小的元素来生成下标较大的元素,故可以让第二层循环的循环变量j由大到小,
这样就可以用一个一维数组B[]来代替pre[]和cur[]来存储解了,即数组B中下标较小的元素B[j-W[i]]相当于pre[j-W[i]],
而B[j]相当于cur[j],当(j < W[i] || B[j] > B[j-W[i]] + P[i])时,B[j]的值不变,否则B[j] = B[j-W[i]] + P[i];。 
*/
#include<iostream>
#include<cmath>

using namespace std;

const int MAXC = 12880; //背包最大容量 
const int MAXN = 3402; //物品的个数
int W[MAXN+1];//物品的重量 
int P[MAXN+1];//物品的价值 
int pre[MAXC+1]; //记录上一行元素值
int cur[MAXC+1]; //记录当前行元素值
int B3[MAXC+1]; //记录最优解 
int B1[MAXN+1][MAXC+1]; //备忘录,记录给定N个物品装入容量为C的背包的最大价值 
int B2[MAXN+1][MAXC+1]; //备忘录,记录给定N个物品装入容量为C的背包的最大价值 

int Best_1(int n, int c); //计算给定n个物品装入容量为c的背包的最大价值  
int Best_2(int n, int c);//动态规划:自底而上,获得给定n个物品装入容量为c的背包的最大价值 
int Best_3(int n, int c);//优化的动态规划算法
int Best_4(int n, int c);//进一步优化的动态规划算法

int main() 
{
	int n, c;
	cin >> n >> c;
	
	for (int i=1; i<=n; i++)//不计下标为0的元素 
	{
		cin >> W[i] >> P[i];
	}
	
	for (int i=1; i<=n; i++)//初始化为-1,表示还没有存储该备忘录 
 	{
	 	for (int j=1; j<=c; j++)
	 	{
		 	B1[i][j] = -1;
		}
	}
	
	cout << Best_1(n, c) << endl;
	cout << Best_2(n, c) << endl;
	cout << Best_3(n, c) << endl;
	cout << Best_4(n, c) << endl;
	
	return 0;
}

int Best_1(int n, int c) //计算给定n个物品装入容量为c的背包的最大价值  
{
 	if (B1[n][c] != -1)  //如果这个问题曾经计算过,直接返回 
 	{
		return B1[n][c];
	}
	
	int bestP = 0;
	if (n == 1)//处理第1个物品,即只有一个物品 
	{
		bestP = (c >= W[n]) ? P[n] : 0;
	}
	else
	{
		bestP = Best_1(n-1, c); //先计算不装第n个物品的情形 
		if (c >= W[n])//如果装得下,从装和不装两者中去最大值 
		{
		    bestP = max(bestP, Best_1(n-1, c-W[n])+P[n]);
		}
	}
	B1[n][c] = bestP;//做备忘录 
	
    return bestP;
}

int Best_2(int n, int c)//动态规划:自底而上,获得给定n个物品装入容量为c的背包的最大价值 
{
	//记录前i(i>=1)个物品装入容量为0-c的背包的最大价值 
 	for (int i=1; i<n; i++)
	{
		for (int j=0; j<=c; j++)
		{
			B2[i][j] = B2[i-1][j]; 
			if (j >= W[i] && B2[i-1][j] < B2[i-1][j-W[i]] + P[i])
				B2[i][j] = B2[i-1][j-W[i]] + P[i];
		}
	}
	//第n个物品只需考虑容量为c的一种情况
	B2[n][c] = B2[n-1][c]; //先默认为不装第n个物品 
	if (c >= W[n]) //如果容量足够装第n个物品 
	{
		B2[n][c] = max(B2[n-1][c], B2[n-1][c-W[n]]+P[n]);
	}
	
	return B2[n][c];
}

int Best_3(int n, int c)//优化的动态规划算法,使用2个一维数组代替二维数组 
{
	//记录前i(i>=1)个物品装入容量为0-c的背包的最大价值 
 	for (int i=1; i<n; i++)
	{
		for (int j=0; j<=c; j++)
		{
			if (j < W[i] || pre[j] > pre[j-W[i]] + P[i])
				cur[j] = pre[j]; 
			else
				cur[j] = pre[j-W[i]] + P[i];
		}
		for (int j=0; j<=c; j++)
		{
			pre[j] = cur[j]; 
		}
	}
	//第n个物品只需考虑容量为c的一种情况
	cur[c] = pre[c]; //先默认为不装第n个物品 
	if (c >= W[n] && pre[c] < pre[c-W[n]] + P[n]) //如果容量足够装第n个物品 
	{
		cur[c] = pre[c-W[n]] + P[n];
	}
	
	return cur[c];
}


int Best_4(int n, int c)//优化的动态规划算法,使用1个一维数组代替二维数组 
{
	//记录前i(i>=1)个物品装入容量为0-c的背包的最大价值 
 	for (int i=1; i<n; i++)
	{
		for (int j=c; j>0; j--)
		{//当(j < W[i] || B3[j] > B3[j-W[i]] + P[i])时,B3[j]的值不变
			if (j >= W[i] && B3[j] < B3[j-W[i]] + P[i])
				B3[j] = B3[j-W[i]] + P[i];
		}
	}
	//第n个物品只需考虑容量为c的一种情况
	if (c >= W[n] && B3[c] < B3[c-W[n]] + P[n]) //如果容量足够装第n个物品 
	{
		B3[c] = B3[c-W[n]] + P[n];
	}
	
	return B3[c];
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值