动态规划

动态规划在查找有很多重叠子问题的情况的最优解时有效。它将问题重新组合成子问题。为了避免多次解决这些子问题,它们的结果都逐渐被计算并被保存,从简单的问题直到整个问题都被解决。因此,动态规划保存递归时的结果,因而不会在解决同样的问题时花费时间。

 1. 斐波纳契数

  • 非递归实现
#include <stdio.h>
/* 
利用 迭代 计算 斐波那契数 
其形式为: 1, 1, 2, 3, 5, 8, 13, 21, 34 … 
*/
long fibonacci(int n){
    long result;
    long previous;
    long next;
    
    next = previous = 1;
    
    if(n<=2){
        return 1;
    }
    int i =3; /* 用于计算已经计算到第几个 */ 
    while( i<=n ){
        result = previous + next;
        previous = next;
        next = result;
        i++;
    } 
    return result;    
}
  • 递归实现
int Fibonacci(int i)
{
    if(i<1) return 0;
    if(i==1) return 1;
    return Fibonacci(i-1)+Fibonacci(i-2);
}

但是这个递归实现是一个指数时间的算法,因为例如:在计算fibonacci(10) 时,fibonacci(3)被计算了21次;但在计算fibonacci(30) 时,fibonacci(3)被计算了 317811 次;这 317811 次计算结果完全一样,除了一次有用外,其余都是浪费。

那我们怎样改进呢?这就用到我们今天所要用到的动态规划。程序改进如下:

//斐波纳契数(动态规划的递归实现)
//主要思想是:通过把递归产生的值保存到一个数组中,只要数组中存在就直接返回,避免重新计算
int Fibonacci(int i,long knownF[])
{
    int t;
    if(knownF[i]!=0)
        return knownF[i];
    if(i==0) t=0;
    if(i==1) t=1;
    if(i>1) t=Fibonacci(i-1,knownF)+Fibonacci(i-2,knownF);
    knownF[i]=t;
    return knownF[i];
}

改进前程序复杂度为O(φN)(φ为黄金分割率1.618...),但是动态递归之后就变为线性的了。

 

2. 背包问题

  • 背包问题的递归实现
//背包问题(递归实现)--非0-1背包
typedef struct { int size; int val} Item; //物品结构:包括大小和价值
int knap(int cap) //cap容量 N种物品
{
    int i,space,max,t;
    for (i=0,max=0; i < N; ++i)
    {
        if((space=cap-items[i].size)>=0)
            if((t=knap(space)+items[i].val)>=max)
                max=t;
    }
    return max;
}
  • 背包问题的动态规划递归实现
//M容量,maxKnow[i]保存容量为i大小的max

//Item items[N]={{3,4,'a'},{4,5,'b'},{7,10,'c'},{8,11,'d'},{9,13,'e'}};
//Item itemKnown[M+1]; //保存选出的最优解的物品
//int maxKnowm[M+1];

int knap(int M)
{
    int i,space,max,maxi=0,t;
    if(maxKnowm[M]!=0) return maxKnowm[M];
    for (i=0,max=0; i < N; ++i)
    {
        if((space=M-items[i].size)>=0)
            if((t=knap(space)+items[i].val)>=max)
            {
                max=t;
                maxi=i; 
            }

    }
    maxKnowm[M]=max;
    itemKnow[M]=Items[maxi];
    return max;
}

我们保存了最优解的索引,因此可以把背包的内容输出。

itemKnow[M]是背包为M大小的时候,最优背包中的一个物品。这个物品的大小为itemKnow[M].size。如果把这个物品从最优解中去掉,就成了求解M-itemKnow[M].size大小的背包最优解的问题。同理,按照程序,itemKnow[M-itemKnow[M].size]是背包为M-itemKnow[M].size大小的时候,最优解中的一个物品。这样不断递归就能输出最优背包中的物品了。

  • 0-1背包的动态规划问题

本程序转自:http://wenku.baidu.com/view/5d1aa737eefdc8d376ee3247.html###

0/1背包问题求解
/************************************************************************
  * 0/1背包问题求解 (visual studio 2005)
  * 给定一个载重量为m,及n个物品,其重量为wi,价值为vi,1<=i<=n
  * 要求:把物品装入背包,并使包内物品价值最大
  ************************************************************************/
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <string>
 using namespace std;

 #define FILENAMELENGTH 100
 
 class CBeibao
 {
 public:
     int m_nNumber;        //物品数量
     int m_nMaxWeight;    //最大载重量
 
     int *m_pWeight;        //每个物品的重量
     int *m_pValue;        //每个物品的价值
 
     int *m_pCount;        //每个物品被选中的次数
     int m_nMaxValue;    //最大价值
 
 public:
     CBeibao(const char *filename);
     ~CBeibao();
     
     int GetMaxValue();
     int GetMaxValue(int n,int m,int *w,int *v,int *c);
     void Display(int nMaxValue);
     void Display(int nMaxValue,const char *filename);
 };
 
 //读入数据
 CBeibao::CBeibao(const char *filename)
 {
     FILE *fp=fopen(filename,"r");    
     if(fp==NULL)
     {
         printf("can not open file!");
         return;    //exit(0);
     }
 
     //读入物品数量和最大载重量
     fscanf(fp,"%d%d",&m_nNumber,&m_nMaxWeight);
 
     m_pWeight=new int[m_nNumber+1];
     m_pValue=new int[m_nNumber+1];
 
     m_pWeight[0]=0;
     //读入每个物品的重量
     for(int i=1;i<=m_nNumber;i++)
         fscanf(fp,"%d",m_pWeight+i);
 
     m_pValue[0]=0;
     //读入每个物品的价值
     for(int i=1;i<=m_nNumber;i++)
         fscanf(fp,"%d",m_pValue+i);
 
     //初始化每个物品被选中次数为0
     m_pCount=new int[m_nNumber+1];
     for(int i=0;i<=m_nNumber;i++)
         m_pCount[i]=0;
 
     fclose(fp);
 }
 
 CBeibao::~CBeibao()
 {
     delete[] m_pWeight;
     m_pWeight=NULL;
     delete[] m_pValue;
     m_pValue=NULL;
     delete[] m_pCount;
     m_pCount=NULL;
 }
 
 /************************************************************************
  * 动态规划求出满足最大载重量的最大价值
  * 参数说明:n:物品个数
  *           m:背包载重量
  *           w:重量数组
  *           v:价值数组
  *           c:是否被选中数组
  * 返回值:最大价值
  ************************************************************************/
 int CBeibao::GetMaxValue(int n,int m,int *w,int *v,int *c)
 {
     int row=n+1;
     int col=m+1;
 
     int i,j;    //循环变量:前i个物品能够装入载重量为j的背包中
 
     //value[i][j]表示前i个物品能装入载重量为j的背包中物品的最大价值
     int **value=new int*[row];
     for(i=0;i<row;i++)
         value[i]=new int[col];
 
     //初始化第0行
     for(j=0;j<col;j++)
         value[0][j]=0;
 
     //初始化第0列
     for(i=0;i<row;i++)
         value[i][0]=0;
 
     //计算
     for(i=1;i<row;i++)
     {
         for(j=1;j<col;j++)
         {
             //w[i]>j,第i个物品不装入背包
             
                value[i][j]=value[i-1][j];
             //w[i]<=j,且第i个物品装入背包后的价值>value[i-1][j],则记录当前最大价值

                if (w[i]<=j)
                {
                    int temp=value[i-1][j-w[i]]+v[i];
                 if(w[i]<=j && temp>value[i][j])
                     value[i][j]=temp;
                }
         }
     }
 
     //逆推求装入的物品
     j=m;
     for(i=row-1;i>0;i--)
     {
         if(value[i][j]>value[i-1][j])
         {
             c[i]=1;
             j-=w[i];
         }
     }
 
     //记录最大价值
     int nMaxVlaue=value[row-1][col-1];
 
     //释放该二维数组
     for(i=0;i<row;i++)
     {
         delete [col]value[i];
         value[i]=NULL;
     }
     delete[] value;
     value=NULL;
 
     return nMaxVlaue;
 }
 
 int CBeibao::GetMaxValue()
 {
     int nValue=GetMaxValue(m_nNumber,m_nMaxWeight,m_pWeight,m_pValue,m_pCount);
     m_nMaxValue=nValue;
     return nValue;
 }
 
 //显示结果
 void CBeibao::Display(int nMaxValue)
 {
     printf("   %d \n",nMaxValue);
     for(int i=1;i<=m_nNumber;i++)
     {
         if(m_pCount[i])
             printf("  %d  %d \n",i,m_pCount[i]);
     }
     printf(" ");
 }
 
 void CBeibao::Display(int nMaxValue,const char *filename)
 {
     FILE *fp=fopen(filename,"w");    
     if(fp==NULL)
     {
         printf("can not write file!");
         return;    //exit(0);
     }
 
     fprintf(fp,"%d \n",nMaxValue);
     for(int i=1;i<=m_nNumber;i++)
     {
         if(m_pCount[i])
             fprintf(fp,"%d  %d\n",i,m_pCount[i]);
     }
     fclose(fp);
 }
 
 //显示菜单
 void show_menu()
 {
     printf("--------------------------------------------- \n");
     printf("input command to test the program \n");
     printf("   i or I : input filename to test \n");
     printf("   q or Q : quit \n");
     printf("--------------------------------------------- \n");
     printf("$ input command >");
 }
 
 void main()
 {
     char sinput[10];
     char sfilename[FILENAMELENGTH];
 
     show_menu();
 
     scanf("%s",sinput);
     while(stricmp(sinput,"q")!=0)
     {
         if(stricmp(sinput,"i")==0)
         {
             printf("  please input a filename:");
             scanf("%s",sfilename);
 
             //获取满足最大载重量的最大价值
             CBeibao beibao(sfilename);
             int nMaxValue=beibao.GetMaxValue();
             if(nMaxValue)
             {
                 beibao.Display(nMaxValue);
                 int nlen=strlen(sfilename);
                 strcpy(sfilename+nlen-4,"_result.txt");
                 beibao.Display(nMaxValue,sfilename);
             }
             else
                 printf("   error! please check the input data! ");
         }
 
         //输入命令
         printf("\n$ input command >");
         scanf("%s",sinput);
     }
 }

 

posted @ 2012-07-31 18:52  csqlwy  阅读(661)  评论(0编辑  收藏  举报