背包问题---递归及动态规划

一、原题


如果有一组物品,各个物品的质量已知,现有一个背包,背包可以容纳的质量总和S已知,问是否能从这N个物品中取出若干个恰好装入这个背包中。

二、递归算法

本质思想:设法尝试全部组合,当部分组合已经无法满足条件时,马上停止当前组合的尝试;若出现第一个满足条件的组合,马上停止尝试。使用递归回溯法实现。(感觉这东西不是我这样的菜鸟可以说明确的,还得自己慢慢体会,最好的方法就是耐住性子跟踪调试)。

上“酸菜”

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

#define N 7 //物品种类
#define S 15 //背包容量
int w[N+1]={0,1,4,3,4,5,2,7}; //各种物品的质量

bool knap(int s,int n) //s代表背包剩余容量,n代表还未尝试装载的物品种类
{
if(s==0) //恰好装完
{
return true;
}
if(s<0 || (s>0 && n<1)) //不能完毕装载
{
return false;
}

if(knap(s-w[n],n-1)) //当前物品可以装载,则递归
{
printf("%d ",w[n]);
return true;
}
else
{
return knap(s,n-1); //当前物品不能装载,取下一物品进行递归
}
}

int main(void)
{
if(knap(S,N))
{
printf("OK!!!\n");
}
else
{
printf("NO!!!\n");
}

system("pause");
return 0;
}




上面的代码只输出了第一个满足条件的组合,那么如何输出所有的有效组合呢?

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

#define N 7
#define S 15
int w[N+1]={0,1,4,3,4,5,2,7};

//
int save[N+1]={0}; //标记数组

int knap(int s,int n)
{
if(s==0)
{
return 1;
}
if(s<0 || (s>0 && n<1))
{
return 0;
}

if(knap(s-w[n],n-1))
{
//printf("%4d",w[n]);
//
save[n]=1;
return 1;
}
return knap(s,n-1);
}

void showSet(void)
{
for(int i=N;i>0;i--)
{
if(save[i]==1)
{
printf("%4d",w[i]);
}
}
}

int main(void)
{
bool flag;
for(int i=N;i>0;i--) //不断降低物品的种类,以便遍历全部组合
{
//
for(int m=0;m<N+1;m++)
{
save[m]=0;
}

if(knap(S,i))
{
showSet();
printf("\nOK!\n");
//
flag=true; //当物品种类为i时,存在有效组合
}
else
{
//printf("\nNO!\n");
flag=false;
}

//
while(flag)
{
int j=N;
int cnt=0;
int s_index=0;
while(j!=0 && cnt!=2)
{
if(save[j]==1)
{
cnt++;
}
if(cnt==1)
{
s_index=j;
}
j--;
}
if(cnt==2)
{
for(int k=0;k<s_index;k++)
{
save[k]=0;
}

if(knap(S-w[s_index],j)) //从有效组合第二个物品的下一个物品開始寻找
{
showSet();
printf("\nOK!\n");
}
else
{
flag=false;
}

}
else
{
flag=false;
}
}

}

system("pause");
return 0;
}


举例来讲,因为第一个有效的组合是7,2,5,1,所下面一次从2的下一个数開始搜索,背包的容量变成8=15-7.

三、动态规划方法

详见參考1,这里可以将物品的质量作为它的价值,那么假设最大价值dp[N][S]等于S,则说明背包可以恰好装满。

#include <iostream>
using namespace std;
#define N 7
#define S 15
#define max(a,b) a>b?a:b

int w[N+1]={0,1,4,3,4,5,2,7}; //各种物品的质量

int main(void)
{
int i,j;
int dp[N+1][S+1];

memset(dp,0,sizeof(dp));
for(i=1;i<=N;i++)
{
for(j=0;j<S+1;j++)
{
if(j>=w[i])
{
dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+w[i]); //转移方程,当中w[i]能够看做各物品的价值
}
else
{
dp[i][j]=dp[i-1][j];
}
}
}

printf("%d\n",dp[N][S]);
if(dp[N][S]==S)
{
printf("OK!!!\n");
}
else
{
printf("NO!!!\n");
}

system("pause");
return 0;
}


四、其他背包相关问题

依据以上分析,加以变通就可以。

时间: 2024-10-27 07:05:25

背包问题---递归及动态规划的相关文章

算法初级面试题08——递归和动态规划的精髓、阶乘、汉诺塔、子序列和全排列、母牛问题、逆序栈、最小的路径和、数组累加成指定整数、背包问题

第八课主要介绍递归和动态规划 介绍递归和动态规划 暴力递归: 1,把问题转化为规模缩小了的同类问题的子问题 2,有明确的不需要继续进行递归的条件(base case) 3,有当得到了子问题的结果之后的决策过程 4,不记录每一个子问题的解 动态规划 1,从暴力递归中来 2,将每一个子问题的解记录下来,避免重复计算 3,把暴力递归的过程,抽象成了状态表达 4,并且存在化简状态表达,使其更加简洁的可能 图灵引入的是:我不知道怎么算,但是我知道怎么试.知道怎么暴力破解出来. 要学会,练习懂得怎么尝试.

70. Climbing Stairs【leetcode】递归,动态规划,java,算法

You are climbing a stair case. It takes n steps to reach to the top. Each time you can either climb 1 or 2 steps. In how many distinct ways can you climb to the top? Note: Given n will be a positive integer. 题目分析:每次只能走1或2步,问n步的话有多少中走法???? 可以用动态规划和递归解

编程算法 - 背包问题(递归) 代码(C)

背包问题(递归) 代码(C) 本文地址: http://blog.csdn.net/caroline_wendy 题目: 有n个重量和价值分别为w,v的物品, 从这些物品中挑选出总重量不超过W的物品, 求所有挑选方案中价值总和的最大值. 即经典动态规划问题. 可以使用深度优先搜索, 把每个部分都遍历到, 选取最优解, 但不是最好的方法. 代码: /* * main.cpp * * Created on: 2014.7.17 * Author: spike */ /*eclipse cdt, gc

编程算法 - 背包问题(三种动态规划) 代码(C)

背包问题(三种动态规划) 代码(C) 本文地址: http://blog.csdn.net/caroline_wendy 题目參考: http://blog.csdn.net/caroline_wendy/article/details/37912949 能够用动态规划(Dynamic Programming, DP)求解, 能够通过记忆化搜索推导出递推式, 能够使用三种不同的方向进行求解. 动态规划主要是状态转移, 须要理解清晰. 代码: /* * main.cpp * * Created o

01背包问题回溯法和动态规划

题目要求: 输入背包的容量v和物品的数量n:接下来n 行每行输入两个数字,第一个是物品质量,第二个是物品价值: 输出背包容纳物品的最大价值. 下面直接贴代码: 回溯法 1 #include<iostream>//之前必须知道背包容量和n个物品 2 #include<algorithm> 3 using namespace std; 4 class Property 5 { 6 public: 7 int weight,profit; 8 double average; 9 frie

斐波那契数列的实现(简单递归和动态规划)

斐波那契数列的实现(简单递归和动态规划) 一.简单递归的实现 1 #include "stdafx.h" 2 #include <string> 3 using namespace std; 4 int f(int n) 5 { 6 if (n == 0) 7 { 8 return 0; 9 } 10 if (n == 1) 11 { 12 return 1; 13 } 14 return f(n - 1) + f(n - 2); 15 } 16 int _tmain(in

利用递归和动态规划来求解组合数

组合数定义:从m个不同元素中,任取n(n≤m)个元素并成一组,叫做从m个不同元素中取出n个元素的一个组合:从m个不同元素中取出n(n≤m)个元素的所有组合的个数,叫做从m个不同元素中取出n个元素的组合数. 下面是一种比较通俗的计算公式: 其递归公式为: c(n,m)=c(n-1,m-1)+c(n-1,m) 下面是c++实现该递归算法: #include <iostream> #include <stdlib.h> #define EXIT -1 using namespace st

左神算法第八节课:介绍递归和动态规划(汉诺塔问题;打印字符串的全部子序列含空;打印字符串的全排列,无重复排列;母牛数量;递归栈;数组的最小路径和;数组累加和问题,一定条件下最大值问题(01背包))

暴力递归: 1,把问题转化为规模缩小了的同类问题的子问题 2,有明确的不需要继续进行递归的条件(base case) 3,有当得到了子问题的结果之后的决策过程 4,不记录每一个子问题的解 动态规划 1,从暴力递归中来 2,将每一个子问题的解记录下来,避免重复计算 3,把暴力递归的过程,抽象成了状态表达 4,并且存在化简状态表达,使其更加简洁的可能 一:递归 1. 汉诺塔问题 汉诺塔问题(不能大压小,只能小压大),打印n层汉诺塔从最左边移动到最右边的全部过程. 左中右另称为 from.to.hel

【动态规划专题】1:斐波拉契数列问题的递归和动态规划

<程序员代码面试指南--IT名企算法与数据结构题目最优解> 左程云 著 斐波拉契数列问题的递归和动态规划 [题目]:给定整数N,返回斐波拉契数列的第N项.补充问题1:给定整数N,代表台阶数,一次可以跨2个或者1个台阶,返回有多少种走法.补充问题2:假设农场中成熟的母牛每年只会生产1头小母牛,并且永远不会死.第一年农场只有1只成熟的母牛,从第2年开始,母牛开始生产小母牛.每只小母牛3年后成熟又可以生产小母牛.给定整数N,求出N年后牛的数量. [举例]斐波拉契数列f(0)=0, f(1)=1,f(