`
445822357
  • 浏览: 736603 次
文章分类
社区版块
存档分类
最新评论

动态规划入门三---背包问题(1)

 
阅读更多

序言

背包问题是最广为人知的动态规划问题之一,拥有很多的变形,尽管在理解之后不难写出程序,但是往往要花费一定的时间真正的掌握它。

多阶段决策问题

1引例

物品无限的背包问题
有n种物品,每种均为无穷多个。第i个物品的体积为Vi,重量为Wi。选一些物品装入一个容量为C的背包,是的背包内的物品在总体积不超过C的情况下重量尽可能地大。
1<= n<= 100, 1<=C<=10000, 1<= Wi <= 1000000

【分析】
似乎很眼熟,因为这个就是之前的DAG的硬币问题扩展版,不过增加了一个属性重量。即是原来的无权图便成了有权图。
问题便成了求以C为起点的(终点任意)的,边权之和最大的路径。
此时最大的变化就是把原来的+1,变成了 + W[ i ] 。

2 0--1背包问题

有n种物品,每种只有一个。第i个物品的体积为Vi,重量为Wi。选一些物品装入一个容量为C的背包,是的背包内的物品在总体积不超过C的情况下重量尽可能地大。
1<= n<= 100, 1<=C<=10000, 1<= Wi <= 1000000 。

【分析】
此时可以发现,原来的只靠“剩余体积”这个状态,无法再做此题,因为不知道每个物品是否已经被用过。
也即是原来的状态转移太过混乱,任何时候均可以使用任何物品,所以,我们要把决策有序化!

【解决思想】

引入阶段的概念,具体参见回溯法的理解。

【算法设计】
用d(i,j) 表示当前在第i层,背包剩余剩余容量为j时候接下来的最大重量和,则就分为两种情况:第一种是把第j个物品放进去,此时背包的重量是d(i+1,j -V[ i ] ) + W[ i ] ,
第二种是不放第j个,此时背包的重量是:d(i+1,j)。
所以就有了下面的式子:
d(i,j) = max { d( i+1,j) ,d(i+1,j -V[ i ] ) + W[ i ] }

注意:这个递推的边界处理。下面的代码里面有提及。

提示:多阶段决策的最优化问题往往可以用动态规划来解决,其中,状态及其转移类似于回溯法的解答树。解答树中的“层数”,即是递归函数里面的“当前填充位置”,描述的是即将完成的决策序号,在DP中被称为阶段。。。

其实:说的白一点,所谓的d(i,j)代表的就是把第i个以及其后的n-i+1个物品装到容量为j的背包中的最大重量。

【算法程序代码】

第一个方法:递推法
关键代码如下:

	printf("请输入%d个物品的体积和重量:",n) ;
		for (i=1; i<=n; i ++)
		{
			scanf("%d%d", &v, &wei) ; // 新的状态定义f(i,j)允许我们边读入边计算,而不必把数值存入数组
			for (j=0; j<=c; j++)
			{
				f[i][j] = (i==1 ? 0 : f[i-1][j] );
				if (j >= v)
					if(f[i][j] < f[i-1][j-v] + wei)
						f[i][j] = (f[i-1][j-v] + wei);
			}
		}


第二个方法,一维数组解决。
先上代码:
    for(i=0;i<=n;i++)
    {
        for(j=c;j>=V[i];j--)
        {
            WEI[j]=max(WEI[j],WEI[j-V[i]]+ W[i]);
        }
    }
    printf("第一种的计算结果是:%d\n\n", WEI[c]);


实例实践

加入背包容积为5,
各个物品的体积为2,4,1
各个物品的重量为9,8,1
测试代码如下:

/***** 背包问题 ********/

/******** written by C_Shit_Hu ************/

////////////////动态规划///////////////

/****************************************************************************/
/* 
有n种物品,每种只有一个。第i个物品的体积为Vi,重量为Wi。
选一些物品装入一个容量为C的背包,是的背包内的物品在总体积不超过C的情况下重量尽可能地大。
1<= n<= 100,  1<=C<=10000, 1<= Wi <= 1000000 。
*/
/****************************************************************************/


// 三种算法
#include <stdio.h>
#include <string.h>
#define MAX 100
int V[MAX] , W[MAX],f[MAX][MAX], WEI[MAX] ;
int n, i, j, c, v, wei, ele; 

int max(int x,int y)
{return x>y ? x:y;}

int main()
{
	// 基本信息的输入
	printf("请输入您的背包的容积:");
	scanf("%d", &c);
	printf("请输入您要装入背包的物品种类数目:");
	scanf("%d", &n);
	
	// 第一种第二种需要的数据
	printf("请输入每种物品的体积(以空格隔开):");
	for (i=0; i<n; i++)
		scanf("%d", &V[i]);
	printf("请输入每种物品的重量(以空格隔开):");
	for (i=0; i<n; i++)
		scanf("%d", &W[i]);
	/* 原理介绍
	另外一种对称的状态定义:用f(i,j)表示“把前i个物品装入容量为j的背包中的最大重量和”,由此也可得其状态转移方程:
	f(i,j) == max ( f(i-1), f(i-1,j-V[i]) + W[i] )    // 同样是分为装或不装第i个
	边界是类似的:i=0时为0,j<0时为负无穷,最终答案是f(n, c).
	*/
	// 代码如下:
	for(i=0;i<=n;i++)
    {
        for(j=c;j>=V[i];j--)
        {
            WEI[j]=max(WEI[j],WEI[j-V[i]]+ W[i]);
        }
    }
    printf("第一种的计算结果是:%d\n\n", WEI[c]);
	printf("请输入%d个物品的体积和重量:",n) ;
	for (i=1; i<=n; i ++)
	{
		scanf("%d%d", &v, &wei) ; // 此处就是两种方法的区别:新的状态定义f(i,j)允许我们边读入边计算,而不必把数值存入数组
		for (j=0; j<=c; j++)
		{
			f[i][j] = (i==1 ? 0 : f[i-1][j] );
			if (j >= v)
				if(f[i][j] <= f[i-1][j-v] + wei)
					f[i][j] = (f[i-1][j-v] + wei);
		}
	}
	printf("第二种的计算结果是:%d\n\n", f[n][c]);
	return 0;
}


/******************************************************/
/********************  心得体会  **********************/
/*

*/
/******************************************************/

测试结果如下:



问题求助


下面代码的注释部分,觉得算法没有问题,为何就是结果不对呢?

/***** 背包问题 ********/

/******** written by C_Shit_Hu ************/

////////////////动态规划///////////////

/****************************************************************************/
/* 
有n种物品,每种只有一个。第i个物品的体积为Vi,重量为Wi。
选一些物品装入一个容量为C的背包,是的背包内的物品在总体积不超过C的情况下重量尽可能地大。
1<= n<= 100,  1<=C<=10000, 1<= Wi <= 1000000 。
*/
/****************************************************************************/


// 三种算法
#include <stdio.h>
#include <string.h>
#define MAX 100
int V[MAX] , W[MAX], d[MAX][MAX], f[MAX][MAX], h[MAX], WEI[MAX] ;
int n, i, j, c, v, wei, ele; 

int max(int x,int y)
{return x>y ? x:y;}

int main()
{
	// 基本信息的输入
	printf("请输入您的背包的容积:");
	scanf("%d", &c);
	printf("请输入您要装入背包的物品种类数目:");
	scanf("%d", &n);
	
	// 第一种第二种需要的数据
	printf("请输入每种物品的体积(以空格隔开):");
	for (i=0; i<n; i++)
		scanf("%d", &V[i]);
	printf("请输入每种物品的重量(以空格隔开):");
	for (i=0; i<n; i++)
		scanf("%d", &W[i]);
		/*   for (i=0; i<n; i++)
		printf("%d  ", V[i]);
		for (i=0; i<n; i++)
        printf("%d ",W[i]);
		// 第一种解决方案:递推思想解决,答案是d[1][c]
		for (i=n; i>=1; i--)
		for (j=0; j<=c; j++)
		{
		d[i][j] = (i==n ? 0 : d[i+1][j] );
		if (j >= V[i])
		if(d[i][j] < (d[i+1][j-V[i]] + W[i]))
		d[i][j] = ( d[i+1][j-V[i]] + W[i]);
		}
		for (i=n; i>=1; i--)
		for (j=0; j<=c; j++)
		printf("%d  ",d[i][j] );
		printf("\n\n第一种的计算结果是:%d\n\n", d[1][c]);
	*/	
	// 改变方向
	/* 原理介绍
	另外一种对称的状态定义:用f(i,j)表示“把前i个物品装入容量为j的背包中的最大重量和”,由此也可得其状态转移方程:
	f(i,j) == max ( f(i-1), f(i-1,j-V[i]) + W[i] )    // 同样是分为装或不装第i个
	边界是类似的:i=0时为0,j<0时为负无穷,最终答案是f(n, c).
	*/
	// 代码如下:
	for(i=0;i<=n;i++)
    {
        for(j=c;j>=V[i];j--)
        {
            WEI[j]=max(WEI[j],WEI[j-V[i]]+ W[i]);
        }
    }
    printf("第一种的计算结果是:%d\n\n", WEI[c]);
	printf("请输入%d个物品的体积和重量:",n) ;
	for (i=1; i<=n; i ++)
	{
		scanf("%d%d", &v, &wei) ; // 此处就是两种方法的区别:新的状态定义f(i,j)允许我们边读入边计算,而不必把数值存入数组
		for (j=0; j<=c; j++)
		{
			f[i][j] = (i==1 ? 0 : f[i-1][j] );
			if (j >= v)
				if(f[i][j] <= f[i-1][j-v] + wei)
					f[i][j] = (f[i-1][j-v] + wei);
		}
	}
	printf("第二种的计算结果是:%d\n\n", f[n][c]);
	/*
	//滚动数组方式
	// 代码如下:
	memset(h, 0, sizeof(h)) ;
	printf("请输入%d个物品的体积和重量:", n);
	for(i=1; i<=n; i++)
	{
	scanf("%d%d", &v, &wei) ; 
	for(j=c; j>=0; j--)
	if(j>=v)
				if (h[j] >= h[j-v]+wei)
				h[j] = h[j-v] + wei;
				}
				printf("第三种的计算结果是:%d\n", h[c]);
				
				  return 0 ;
	*/
}


/******************************************************/
/********************  心得体会  **********************/
/*

*/
/******************************************************/

下一步。。。经典背包问题九讲的学习。

【未完待续……】








分享到:
评论

相关推荐

    动态规划背包问题入门

    动态规划入门,hdu上的动态规划入门题的结题报告。 hdu 1171,hdu 1059,hdu 2159,hdu 2191,hdu 3496

    动态规划_01背包_01背包_C++_算法_背包_动态规划_

    C++动态规划实现01背包算法入门通过二维表的方式实现01背包的选取问题

    背包九讲动态规划入门

    动态规划(dynamic programming)是运筹学的一个分支,是求解决策过程(decision process)最优化的数学方法。20世纪50年代初美国数学家R.E.Bellman等人在...逐个求解,创立了解决这类过程优化问题的新方法——动态规划。

    背包问题9讲

    动态规划 指导入门资料

    经典的动态规划入门练习题

    计算精确到分(1元=100分)。编写程序估计实际行驶在某路线所需的最小费用。 输入格式: 从当前目录下的文本文件“route.dat”读入数据。按以下格式输入若干旅行路线的情况: 第一行为起点到终点的距离(实数) 第二...

    01背包C语言

    0 1背包 C,使用动态规划算法,复杂度O(N*N) 非递归,代码简洁,便于初学者学习,C语言入门学习

    01背包.cpp

    01背包的解法,详细介绍了如何动态规划的初步入门,如何找到最优子集,找到最优的解法。

    背包九讲v1.1 PDF版

    背包问题是动态规划中的典型 而动规又是信息学中的重中之重 文档从基础的背包问题逐次加深,为你解开动态规划的神秘面纱,谨以此作为学习动态规划的入门吧 感谢 dd_engi大牛的分享 PDF版本的供大家下载

    算法基础入门:90分钟搞定动态规划

    动态规划(Dynamic programming,简称DP)很多人都觉得是比较难以理解和掌握的一种算法,为了应付面试更多的时候程序员会选择直接死记硬背斐波那楔数列或者背包问题的源码,其实只要认真学习、彻底理解,动态规划并...

    MATLAB-常用数学建模算法大全(26种):理论介绍+源代码+实例分析

    6. MATLAB: 0-1规划(背包问题) 7. MATLAB-线性规划求最优解 8. MATLAB-随机森林实现数据回归分析预测 9. MATLAB-GM(1,N)灰色预测模型的建立 10. MATLAB-偏最小二乘回归分析 11. MATLAB-基于灰色神经网络的预测...

    1.01背包 取一次_背包问题_

    01背包洛谷例题精选,适合入门算法的同学食用

    有关于背包问题源代码

    背包问题的源代码,希望对刚入门的人有所帮助

    Acm常用算法学习模板-1

    文件包括以下子文件,每个文件里面包括了一定数量的ppt,doc,c++模板代码,希望对算法的入门的学习者有用。(注:其中多数文件是download它人的,本人只是将其整理) 00-经典错误 0-广度优先搜索 0-深度优先搜索 1-...

    背包入门1

    第 i 个物品的成本和收益分别是 ci 和每个物品最多能拿 1 个。第 i 个物品的成本和收益分别是 ci 和每个物品最多能拿 ∞ 个。第 i 个物品的成本和收

    acm杭电入门课件+博弈论文+背包九讲

    acm杭电课件+博弈的一篇英文论文+背包九讲的论文

    算法九讲2.0

    经典的动态规划入门,包括01背包、完全背包、多重背包的详解

    C语言从入门到精通视频教程下载第22章 背包问题求解.zip

    视频教程

    计算机考研机试攻略 - 高分篇(试读).pdf

    目录 第一章 从零开始 8 1.1机试分析 8 1.2 IDE的选择与评测结果 10 1.3 DreamJudge的使用 11 1.4输入输出技巧 12 ...1.9多组输入的问题 27 ...第二章 入门经典 29 ...8.7 字符串相关的动态规划 182

    2.完全背包 取无限次_完全背包_

    完全背包算法洛谷例题精选,适合入门算法同学食用!

    mknapsack:用遗传算法求解多维背包问题

    多维背包 0-1 求解器 康斯坦茨应用科学大学曲荣女士的人工智能课程作业 2。 任务是设计和实现基于人口的算法来解决具有多个约束的背包问题。 使用的基准可以在“另请参阅”部分下找到。 它是 OR 库。 入门 运行“npm...

Global site tag (gtag.js) - Google Analytics