⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 0-1注解.txt

📁 算法设计与分析:动态规划解决0-1背包问题
💻 TXT
字号:
/**
* 程序名称:动态规划解0-1背包问题(C++语言版). 
*
* 说明:如果该程序进行了更新,可以在我的博客 http://yifanwq.itpub.net/  下载到最新的版本
*
* 该程序并非完全免费  对所有阅读该程序的用户实行 '良心付费 ' 的授权方法.
*       ①如果你认为该程序对你没用,或者你根本看不懂,请提出你的意见,联系QQ:442000868 
*       ②如果该程序对您理解 动态规划算法确实起来了一定的帮助作用,请作者美餐一顿吧!还是联系QQ:442000868 
*	             	┌───────────────────────┐
*                   │ 您的意见或您的美餐是作者最大的编程动力!!! │
*					└───────────────────────┘
*/
#include <cstdlib>
#include <string>
#include <iostream>

//定义背包的容量
#define   C    9  

using namespace std;

//求最大值函数
int max(int x,int y)  
{
	return x >= y?x:y;
}

//求最小值函数
int min(int x,int y)
{
	return x>= y ? y:x;
}

/*求出背包解的备忘录矩阵
* v[] 价值数组
* w[] 物品的重量数组
* c   背包的容量
* n   物品的个数
* m   用于记录每步结果的备忘录矩阵(m是一个以物品的个数作为行数,
	  以总容量作为列数,例如有4个物品,背包的容量是10则该数组就应定义一个4行10列的数组,
	  但为了方便程序编写,常定义为5行11列的数组,第1行和第1列不用)
例如: w[]={0,7,4,6,2}  v[]={0,2,8,4,9}  c=C=9(开始的宏定义),n=4,则m(备忘录矩阵)是:
	┌─────────────────────┐
	│ │ │①│②│③│④│⑤│⑥│⑦│⑧│⑨│
	├─────────────────────┤
	│ │ │ │ │ │ │ │ │ │ │ │
	├─────────────────────┤
	│⑴│ │V │V │V │V │V │V │V │V │V │
	├─────────────────────┤
	│⑵│ │V │V │V │V │V │V │V │V │V │
	├─────────────────────┤
	│⑶│ │V │V │V │V │V │V │V │V │V │
	├─────────────────────┤
	│⑷│ │V │V │V │V │V │V │V │V │V │
	└─────────────────────┘
	行⑴…⑷代表可放入的物品为1…4
	列①…⑨背包的容量分别是1…9
	列代表矩阵中空格代表不会使用的区域(方便程序的理解)

   点(⑵,④)的V值可以这样理解:当背包的容量为4时,可选的物品有⑵,⑶,⑷里,背包的最优解.
   点(⑶,⑨)的V值可以这样理解:当背包的容量为9时,可选的物品有⑶,⑷里,背包的最优解.
   点(⑷,⑧)的V值可以这样理解:当背包的容量为8时,可选的物品只有⑷里,背包的最优解.

  在以下的程序解释中,如果矩阵中未写入具体的值,则代表该值还未进行计算.
*/

void Knaspack(int v[],int w[],int c,int n,int m[][C+1])   
{
	/**
	*----------------------------第一段(Begin)----------------------------------------------
	*该段代码主要填写备忘录矩阵的最后一行,因为前面几行的结果依耐于最后一行
	*该行分别记录当背包的容量分另为1,2…C(C是定义的背包容量)时,可选物品为第最后一个物品时的最优解
	*/

	/**
	*  为什么要用w[n] ? 因为现在要填写的是最后一行,第n行的值代表可选的物品只有最后一个物品时背包的最优解
	*  为什么用w[n]和c一起比较求最小值? 为什么要求出jMax? 为什么要用1..jMax进行循环? 后面会详细解说[AQ1].
	*/
	int jMax=min(w[n]-1,c);    //在本例中,n的当前值是4


	for(int j=1; j <= jMax ; j++)     //具体作用见如下解释
		m[n][j]=0;
	
	/**
	*当前的备忘录矩阵为
	*┌─────────────────────┐
	*│ │ │①│②│③│④│⑤│⑥│⑦│⑧│⑨│
	*├─────────────────────┤
	*│ │ │ │ │ │ │ │ │ │ │ │
	*├─────────────────────┤
	*│⑴│ │V │V │V │V │V │V │V │V │V │
	*├─────────────────────┤
	*│⑵│ │V │V │V │V │V │V │V │V │V │
	*├─────────────────────┤
	*│⑶│ │V │V │V │V │V │V │V │V │V │
	*├─────────────────────┤
	*│⑷│ │0 │V │V │V │V │V │V │V │V │
	*└─────────────────────┘
	*
	*   点(⑷,①)变成为0,表示当背包的容量为1,可选物品为⑷时,最优解为0,代表背包中不能放入任何物品。
	*   现在可以回去看看物品的重量数组,很轻松的就看出来了(最轻的重量为2)。
	*   ┌┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┐
	*   ┆现在来解决上面提出的问题[AQ1] :                                                                   ┆
	*	┆  将w[n]-1与c比较,并给jMax[决定了在可选物品只有第n个物品时,背包的容量 <= jMax时,不能放入任何物品,    
	*	┆ 	即最优解为0]
	*   ┆     更具体解释:1.如果min()函数计算后返回的结果为:jMax=w[n]-1
	*   ┆                        (例中的值为1) --> 说明当背包容量<= 1,可选择物品只有4时,不能放入任何物品
	*  ┆               同时,能能过重量数组也可以轻松看出,背包容量 > = 1时,最后一个物品不能放入.
	*	┆				  2.如果min()函数计算后返回的结果为:jMax=c
	*	┆				         则说明最后一个物品重量 > 背包的容量,也不能放入.故在可选物品只有4的情况下,
	*	┆						 背包的容量 <= c 里,最优解为0
	*	┆						 假如最后一个物品的重量为25即 w[n]=25;则最后一行的应如下
	*	┆						┌─────────────────────┐
	*	┆						│⑷│ │0 │0 │0 │0 │0 │0 │0 │0 │0 │
	*	┆						└─────────────────────┘
	*	┆						表明在只有可选物品为4时,背包的容量在小于c任何情况下,都不能放入,最优解为0;
	*   ┆                                                                                                  ┆
	*  └┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┘
	*/

	
	for(j=w[n]; j <= c ; j++)   //具体作用见如下解释
		m[n][j]=v[n];

	/**
	*当前的备忘录矩阵为
	*┌─────────────────────┐
	*│ │ │①│②│③│④│⑤│⑥│⑦│⑧│⑨│
	*├─────────────────────┤
	*│ │ │ │ │ │ │ │ │ │ │ │
	*├─────────────────────┤
	*│⑴│ │V │V │V │V │V │V │V │V │V │
	*├─────────────────────┤
	*│⑵│ │V │V │V │V │V │V │V │V │V │
	*├─────────────────────┤
	*│⑶│ │V │V │V │V │V │V │V │V │V │
	*├─────────────────────┤
	*│⑷│ │0 │9 │9 │9 │9 │9 │9 │9 │9 │
	*└─────────────────────┘
	* 该循环从w[n]开始到容量c,设置最后一行未求出的最优解V设置为最后一个物品的价值.
	* 图中最后一行所有的9是最后一个物品的价值:代表当可选为⑷, 物品为背包的容量为2..9时,可以将最后一个物品放入.
	*      (请注意查看本程序使用的事例数据) 
	*/

	/**    (第一段的看明白后,有利于后面程序的理解)
	*----------------------------第一段(End)----------------------------------------------
	*/


	/**
	*----------------------------第二段(Begin)----------------------------------------------
	*该程序主要是利用第一行求出的结果并随着可加入物品的增加,来完成备忘录矩阵除第一行外的其它行
	*在该事例程序中,循环共执行两次 每次执行后的结果分别为
	*【第一次】
	*┌─────────────────────┐
	*│ │ │①│②│③│④│⑤│⑥│⑦│⑧│⑨│
	*├─────────────────────┤
	*│ │ │ │ │ │ │ │ │ │ │ │
	*├─────────────────────┤
	*│⑴│ │V │V │V │V │V │V │V │V │V │
	*├─────────────────────┤
	*│⑵│ │V │V │V │V │V │V │V │V │V │
	*├─────────────────────┤
	*│⑶│ │0 │9 │9 │9 │9 │9 │9 │13│13│
	*├─────────────────────┤
	*│⑷│ │0 │9 │9 │9 │9 │9 │9 │9 │9 │
	*└─────────────────────┘
	*【第二次】
	*┌─────────────────────┐
	*│ │ │①│②│③│④│⑤│⑥│⑦│⑧│⑨│
	*├─────────────────────┤
	*│ │ │ │ │ │ │ │ │ │ │ │
	*├─────────────────────┤
	*│⑴│ │V │V │V │V │V │V │V │V │V │
	*├─────────────────────┤
	*│⑵│ │0 │9 │9 │9 │9 │17│17│17│17│
	*├─────────────────────┤
	*│⑶│ │0 │9 │9 │9 │9 │9 │9 │13│13│
	*├─────────────────────┤
	*│⑷│ │0 │9 │9 │9 │9 │9 │9 │9 │9 │
	*└─────────────────────┘
	*/
	for(int i=n-1;i > 1 ; i--)
	{
		/*
		*比较第i个物品的重量各背包的容量
		*/
		jMax=min(w[i]-1,c);

		/**
	    *该代码在第一次循环的过程中,主要处理 点(⑶,①) 到 点(⑶,⑦)的数据:表明在可选物品为⑶,⑷,背包的容量为1..7时,
		*                                     不能放入物品⑶,当前的最优值同只有物品⑷可放入时的最优值相同.当背包的容量
		*第二次循环的处理过程同第一次一样,在意义上只是可选择的物品多了物品⑵							
		*/
		for(j=1; j <= jMax ; j++)
			m[i][j]=m[i+1][j];
		
		/**该代码在第一次循环的过程中,主要处理 点(⑶,⑧) 到 点(⑶,⑨)的数据:表明在可选物品为⑶,⑷,背包的容量为8..9时,
	     *										可以加入物品⑶,加入后的最优值为只有物品⑷的价值加上物品⑶的价值.即为13
	     *第二次循环的处理过程同第一次一样,在意义上只是可选择的物品多了物品⑵									
	     */
		for(j=w[i]; j <= c; j++)
			m[i][j]=max(m[i+1][j],m[i+1][j-w[i]]+v[i]);
	}	
	
	/**
	*----------------------------第二段(End)----------------------------------------------
	*第二段程序执行完成以后,只需要决定第一个物品是不是应该放入,在第三段处理
	*/

	
	/**
	*----------------------------第三段(Begin)----------------------------------------------
	*/

	/*
	*先假设第一个物品不放入
	*/
	m[1][c]=m[2][c]; 

	/*
	*如果背包容量大于第一个物品
	*则此时的最优值决定于 (1)不加入第一个物品 或 (2)不加入上次加入的物品,加入第一个物品  ,最大值。就是最后背包的最优解.
	*/
	if(c > w[1])      
		m[1][c]=max(m[1][c],m[2][c-w[1]]+v[1]);

	/**
	*----------------------------第三段(End)----------------------------------------------
	*/

}

/*
*生成向量数组,决定某一个物品是否应该放入背包
*/
void Traceback(int m[][C+1],int w[],int c,int n,int x[])
{
	for(int i=1; i < n ; i++)   //比较备忘录矩阵两邻两行(除最后一行),背包容量为c的最优值.
	{
		if(m[i][c] == m[i+1][c])   //如果当前行的最优值与下一行的最优值相等,则表明该物品不能放入。
			x[i]=0;
		else                      //否则可以放入
		{
			x[i]=1;
			c-=w[i];
		}
	}
	
	/**单独处理最后一行 
	*  (m[n][c] )?1:0; 等同于(m[n][c] != 0)?1:0;  如果最后一个最优值 为0,则最后一物品不能放入,否则可以放入。
	*/
	x[n]=(m[n][c] )?1:0;

}

//主函数
void main()
{	

	//物品的个数 
	int n=4;

	//背包中物品的重量 (第一个为0,是为了程序理解方便)
	int w[]={0,7,4,6,2};

	//背包中物品的价值(第一个为0,是为了程序理解方便)
	int v[]={0,2,8,4,9};
	
	//备忘录矩阵
	int m[5][C+1];
	
	//背包解的向量数组
	int x[5];

	//构造备忘录矩阵
	Knaspack(v,w,C,n,m);

	//求出解的向量数组
	Traceback(m,w,C,n,x);

	//显示解的结果
	cout<<"背包的容量是:"<<C<<"\t物品的数量是:"<<n<<endl;
	cout<<"重量:\t";
	for(int i=1; i <= n ; i++)
		cout<<w[i]<<"\t  ";
	cout<<endl<<"价值:\t";
	for(i=1; i <= n ; i++)
		cout<<v[i]<<"\t  ";
	cout<<endl<<"背包中应放的物品有:";
	int totalW=0;
	int totalV=0;

	//显示可以放入的物品
	for(i=1; i <= n ; i++)
	{
		if(x[i] == 1)
		{
			totalW+=w[i];
			totalV+=v[i];
			cout<<"(重量:"<<w[i]<<",价值:"<<v[i]<<")"<<"  ";
		}
	}
	cout<<endl<<" >>已放入的物品重量总和是:"<<totalW<<"  价值最优解是:"<<totalV;

	
	cout<<endl<<"如果要查看备忘矩阵,请输入y,退出请输入n?";

	//让用户选择是否显示备忘录矩阵
	char t=getchar();
	if(t == 'y')
	{
		for(i=1; i <= n ; i++)
		{
			for(int j = 1 ; j < C+1; j++)
			{
				int temp=m[i][j]>=0?m[i][j]:-1;
				cout<<temp<<"\t";
			}
			cout<<endl<<"----------------------"<<endl;
		}
	}
	else
	{
		exit(-1);
	}
}






⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -