从01背包问题理解动态规划

  01背包问题具体例子:假设现有容量10kg的背包,另外有3个物品,分别为a1,a2,a3。物品a1重量为3kg,价值为4;物品a2重量为4kg,价值为5;物品a3重量为5kg,价值为6。将哪些物品放入背包可使得背包中的总价值最大?

  这个问题有两种解法,动态规划和贪婪算法。本文仅涉及动态规划。

  先不套用动态规划的具体定义,试着想,碰见这种题目,怎么解决?

  首先想到的,一般是穷举法,一个一个地试,对于数目小的例子适用,如果容量增大,物品增多,这种方法就无用武之地了。

  其次,可以先把价值最大的物体放入,这已经是贪婪算法的雏形了。如果不添加某些特定条件,结果未必可行。

  最后,就是动态规划的思路了。先将原始问题一般化,欲求背包能够获得的总价值,即欲求前i个物体放入容量为m(kg)背包的最大价值c[i][m]——使用一个数组来存储最大价值,当m取10,i取3时,即原始问题了。而前i个物体放入容量为m(kg)的背包,又可以转化成前(i-1)个物体放入背包的问题。下面使用数学表达式描述它们两者之间的具体关系。

  表达式中各个符号的具体含义。

  w[i] :  第i个物体的重量;

  p[i] : 第i个物体的价值;

  c[i][m] : 前i个物体放入容量为m的背包的最大价值;

  c[i-1][m] : 前i-1个物体放入容量为m的背包的最大价值;

  c[i-1][m-w[i]] : 前i-1个物体放入容量为m-w[i]的背包的最大价值;

  由此可得:

      c[i][m]=max{c[i-1][m-w[i]]+pi , c[i-1][m]}(下图将给出更具体的解释)

从01背包问题理解动态规划_第1张图片

 

 

    根据上式,对物体个数及背包重量进行递推,列出一个表格(见下表),表格来自(http://blog.csdn.net/fg2006/article/details/6766384?reload) ,当逐步推出表中每个值的大小,那个最大价值就求出来了。推导过程中,注意一点,最好逐行而非逐列开始推导,先从编号为1的那一行,推出所有c[1][m]的值,再推编号为2的那行c[2][m]的大小。这样便于理解。

从01背包问题理解动态规划_第2张图片

    

    思路厘清后,开始编程序,C语言代码如下所示。

#include <stdio.h>
int c[10][100]={0};

void knap(int m,int n){

    int i,j,w[10],p[10];
    for(i=1;i<n+1;i++)
        scanf("%d,%d",&w[i],&p[i]);
    for(j=0;j<m+1;j++)
        for(i=0;i<n+1;i++)
    {
        if(j<w[i])
        {
            c[i][j]=c[i-1][j];
            continue;
        }else if(c[i-1][j-w[i]]+p[i]>c[i-1][j])
            c[i][j]=c[i-1][j-w[i]]+p[i];
        else
            c[i][j]=c[i-1][j]; } }     


int main(){
    int m,n;int i,j;
    printf("input the max capacity and the number of the goods:\n");
    scanf("%d,%d",&m,&n);
    printf("Input each one(weight and value):\n");
    knap(m,n);
    printf("\n");
   for(i=0;i<=n;i++)
        for(j=0;j<=m;j++)
       {
     printf("%4d",c[i][j]);
    if(m==j) printf("\n");
    }
}
代码中,红色字体部分是自己写的,其余的参照了这篇博客http://blog.sina.com.cn/s/blog_6dcd26b301013810.html

   如果你很轻松地就突破了01背包,甚至很轻松地就理解了动态规划,那么继续前进,做一下这道题目(http://acm.uestc.edu.cn/problem.php?pid=1012)。很好玩的。

 

小结:

   感谢上面引用的两篇博客,也感谢这两位博主,没有你们的博客,我恐怕对01背包问题还是半懂不懂的。

 

ps: 2014/5/19  更新一个递归解法,《算法:c语言》似乎也有一个递归解法,不过是错误的:

#include <stdio.h>
#include <stdlib.h>

typedef struct _Item
{
	int size;
	int val;
}item;

item aitem[5] = { {3, 4}, {4, 5}, {7,10}, {8, 11}, {9, 13} };
int item_flag[5]; // 0: 未访问, 1: 
int result[5] ; // 保存中间输出结果
int mount = 0;
int max;

void  knap_rcs(int cap)
{


	int j ;
	for( j = 0; j < 5; j++)
	{
		int t =  cap - aitem[j].size;
		if( t >= 0 )
		{
			if( item_flag[j] == 0)
			{
				item_flag[j] = 1;
			
				result[j] = aitem[j].size;
				mount += aitem[j].val;

				knap_rcs(t);
				item_flag[j] = 0;
				result[j] = 0;
				mount -= aitem[j].val;
			}

		}
		else // 已到递归终点,输出结果
		{
			int i ;
			for( i =0; i < 5 ; i++)
				if( result[i] != 0 )
					printf("size: %d\t", result[i] );
			printf("\n");
			printf("%d\n", mount);
			if( mount > max )
				max = mount;
			return ;
		}

	}
}
			
		




int main()
{
        int N;

        while(scanf("%d", &N) !=  0)
        {
			getchar();
			for(int k = 0; k < 5; k++)
			{
				item_flag[k] = 0;
				result[k] = 0;
			}

			max = 0;
			knap_rcs(N);
			printf("max value: %d\n\n", max);



                
        }

	system("pause");
        return 0;
}

  

你可能感兴趣的:(动态规划)