动态规划之背包问题

动态规划的逆向思维法

动态规划是一种思维方法,没有统一的、具体的模式。动态规划可以从多方面去考察,不同的方面对动态规划有不同的表述。我们不打算强加一种统一的表述,而是从多个角度对动态规划的思维方法进行讨论,希望大家在思维具体问题时,也能够从多个角度展开,这样收获会更大 ,逆向思维法是指从问题目标状态出发倒推回初始状态或边界状态的思维方法。如果原问题可以分解成几个本质相同、规模较小的问题,很自然就会联想到从逆向思维的角度寻求问题的解决。你也许会想,这种将大问题分解成小问题的思维不就是分治法吗?动态规划是不是分而治之呢?其实,虽然我们在运用动态规划的逆向思维法和分治法分析问题时,都使用了这种将问题实例归纳为更小的、相似的子问题,并通过求解子问题产生一个全局最优值的思路,但动态规划不是分治法:关键在于分解出来的各个子问题的性质不同。
分治法要求各个子问题是独立的(即不包含公共的子问题),因此一旦递归地求出各个子问题的解后,便可自下而上地将子问题的解合并成原问题的解。如果各子问题是不独立的,那么分治法就要做许多不必要的工作,重复地解公共的子问题。
动态规划与分治法的不同之处在于动态规划允许这些子问题不独立(即各子问题可包含公共的子问题),它对每个子问题只解一次,并将结果保存起来,避免每次碰到时都要重复计算。这就是动态规划高效的一个原因。
    动态规划的逆向思维法的要点可归纳为以下三个步骤:
    (1)分析最优值的结构,刻画其结构特征;
    (2)递归地定义最优值;
    (3)按自底向上或自顶向下记忆化的方式计算最优值。
    背包问题描述:
    有一个负重能力为m的背包和n种物品,第i种物品的价值为v[i],重量为w[i]。在不超过背包负重能力的前提下选择若干个物品装入背包,使这些的物品的价值之和最大。每种物品可以不选,也可以选择多个。假设每种物品都有足够的数量。
    分析:
    从算法的角度看,解决背包问题一种最简单的方法是枚举所有可能的物品的组合方案并计算这个组合方案的价值之和,从中找出价值之和最大的方案。显然,这种靠穷举所有可能方案的方法不是一种有效的算法。
    但是这个问题可以使用动态规划加以解决。下面我们用动态规划的逆向思维法来分析这个问题。
    (1)背包问题最优值的结构
    动态规划的逆向思维法的第一步是刻画一个最优值的结构,如果我们能分析出一个问题的最优值包含其子问题的最优值,问题的这种性质称为最优子结构。一个问题的最优子结构性质是该问题可以使用动态规划的显著特征。

    对一个负重能力为m的背包,如果我们选择装入一个第i种物品,那么原背包问题就转化为负重能力为m-w[i]的子背包问题。原背包问题的最优值包含这个子背包问题的最优值。若我们用背包的负重能力来划分状态,令状态变量s[k]表示负重能力为k的背包,那么s[m]的值只取决于s[k](k≤m)的值。因此背包问题具有最优子结构。

问题分析:令V(i,j)表示在前i(1<=i<=n)个物品中能够装入容量为就j(1<=j<=C)的背包中的物品的最大价值,则可以得到如下的动态规划函数:


(1)   V(i,0)=V(0,j)=0 


(2)   V(i,j)=V(i-1,j)  j

        (3) V(i,j)=max{V(i-1,j) ,V(i-1,j-wi)+vi) } j>wi


(1)式表明:如果第i个物品的重量大于背包的容量,则装人前i个物品得到的最大价值和装入前i-1个物品得到的最大价是相同的,即物品i不能装入背包;第(2)个式子表明:如果第i个物品的重量小于背包的容量,则会有一下两种情况:

(a)如果把第i个物品装入背包,则背包物品的价值等于第i-1个物品装入容量位j-wi 的背包中的价值加上第i个物品的价值vi; 

(b)如果第i个物品没有装入背包,则背包中物品价值就等于把前i-1个物品装入容量为j的背包中所取得的价值。显然,取二者中价值最大的作为把前i个物品装入容量为j的背包中的最优解


      

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

动态规划之背包问题_第1张图片


#include
#include
int V[200][200];//前i个物品装入容量为j的背包中获得的最大价值
int max(int a,int b)  //一个大小比较函数,用于当总重大于第I行时 
{
   if(a>=b)
     return a;
   else return b;
}

int Knap(int n,int w[],int v[],int x[],int C)
{
  int i,j;
  for(i=0;i<=n;i++)
    V[i][0]=0;
  for(j=0;j<=C;j++)
    V[0][j]=0;
  for(i=0;i<=n-1;i++)
    for(j=0;j<=C;j++)
      if(j        V[i][j]=V[i-1][j];
      else
        V[i][j]=max(V[i-1][j],V[i-1][j-w[i]]+v[i]);
      j=C;
      for(i=n-1;i>=0;i--)
      {
        if(V[i][j]>V[i-1][j])
        {
        x[i]=1;
        j=j-w[i];
        }
      else
        x[i]=0;   
      }
      printf("选中的物品是:\n");
      for(i=0;i        printf("%d ",x[i]);
      printf("\n");
    return V[n-1][C];
    
}

int main()
{
  int s;//获得的最大价值
  int w[4];//物品的重量   重量  价值  和物品的状态 均对应着存到数组中,物品从1开始。 
  int v[4];//物品的价值
  int x[4];//物品的选取状态   选中则是1  没选中为0 
  int n,i;
  int C;//背包最大容量
  n=4;
  printf("请输入背包的最大容量:\n");
  scanf("%d",&C);
  
  printf("物品数:\n");
  scanf("%d",&n);
  printf("请分别输入物品的重量:\n");
  for(i=0;i    scanf("%d",&w[i]);

  printf("请分别输入物品的价值:\n");
  for(i=0;i    scanf("%d",&v[i]);

  s=Knap(n,w,v,x,C);  //调用核心函数 

  printf("最大物品价值为:\n");
  printf("%d\n",s);
  system("pause");
  return 0;
   
  
}



你可能感兴趣的:(算法,算法与数据结构)