实验报告8 蛮力动态规划01背包

时间:2024.4.14

《算法设计与分析》实验报告八

 

一、实验内容:

分别用蛮力法、动态规划法、回溯法和分支限界法求解0/1背包问题。

二、所用算法的基本思想及复杂度分析:

1、 蛮力法

1) 基本思想

用蛮力法求解就是要穷举出物品的所有组合,计算不超过背包容量的物品的最大价值和

2) 复杂度分析

装法有2n种组合,每种组合中又要计算价值和,所以其复杂度为指数级。

2、 动态规划法

1) 基本思想

动态规划法的关键在于找到决策函数,在本例中,令V(i,j)表示在前i个物品中能够装入容量为j的背包中的物品的最大值,则有如下动态规划函数:

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

            V(i-1,j)                            j<wi

V(i,j)=                                                 (2)

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

式(1)表明:把前面i个物品的装入容量为0的背包和把0个物品装入容量为j的背包,得到的总价值都是0。式(2)的第一个式子表明:如果第i个物品的重量大于背包的容量,则装入前i个物品得到的最大值和装入前i-1个物品得到的最大价值是相同的,即物品i不能装入背包;第二个式子表明:如果第i个物品的重量小于背包的容量,则会有以下两种情况:1.如果把第i个物品装入背包,则背包中的物品价值等于把前i-1个物品装入容量为j-wi的背包中的价值加上第i个物品的价值vi;2.如果第i个物品没有装入背包,则背包中物品的价值就等于把前i-1个物品装入容量为j的背包中所取得的价值。显然取二者的较大值作为把前i个物品装入容量为j的背包中的最优解。

2) 复杂度分析

时间复杂度是O(n*c)。

3、 回溯法

1) 基本思想

回溯法是一种有组织的系统化搜索技术,可以看作是蛮力法穷举搜索的一种改进。具体在0/1背包问题中的使用如下:

回溯法从0/1背包问题的解空间树的根节点出发,按照深度优先遍历解空间树,搜索满足约束条件的解。在搜索到树中的任一结点时,先判断该结点的对应部分的解是否满足约束条件,或者是否超出目标函数的界,也就是判断该结点是否包含问题的(最优)解,如果不包含则跳过对以该结点为根的子树的搜索;否则进入以该结点为根的子树继续深度优先遍历搜索。

2) 复杂度分析

0/1背包问题的解空间树是一棵子集树,遍历一棵子集树所需时间为Ω(2n)。

4、 分支限界法

1) 基本思想

分支限界法按照广度优先法遍历整个解空间树,在遍历过程中,对已经处理过的每一个结点根据限界函数估算目标函数的可能值,从中选取使目标函数取得极大或极小的结点优先进行广度优先搜索,从而不断调整搜索方向,尽快找到问题的最优解。

2) 复杂度分析

由于分治限界法是蛮力法的一种改进,所以在0/1背包问题中,问题的复杂度在最坏的情况下是指数阶的。

三、源程序及注释:

#define MAX 100

#define max(a,b) (a>b)?a:b

#include <MATH.H>

#include <VECTOR>

#include <stdio.h>

#include <stdlib.h>

#include <WINDOWS.H>

#include <TIME.H>

#include <IOSTREAM>

#include <algorithm>

using namespace std;

//设计物品属性,重量和价值

typedef struct{

         double w;          //重量

         double v;                    //价值

}OBJECT;

//回溯法

int bestV=0;               //最大价值

int cw=0;                    //当前重量

int cv=0;                     //当前价值

//进入右子树条件

int R_Bentch(int i,int n,int c,OBJECT t[])

{

         int j=i;

         int left_C=c-cw;

         int tempV=cv;

         while(t[j].w<left_C&&j<n)

         {

                  left_C-=t[j].w;

                  tempV+=t[j].v;

                  j++;

         }

         return tempV;

}

//递归

void BackTrack(int i,int n,int c,OBJECT t[])

{

         if (i>=n)

         {

                  if(bestV<cv)

                           bestV=cv;

                  return;

         }

         if(cw+t[i].w<=c)                  //进入左子树

         {

                  cw+=t[i].w;

                  cv+=t[i].v;

                  BackTrack(i+1,n,c,t);

                  cw-=t[i].w;                           //回溯前回复到上一状态

                  cv-=t[i].v;

         }

         if(R_Bentch(i+1,n,c,t)>bestV)    //进入右子树

                  BackTrack(i+1,n,c,t);

        

}

//分支限界-----------------------------------------

//状态结构体

typedef struct{

         bool  s1[MAX];         //当前放入物体

         int   k;              //搜索深度

         double b;          //价值上界

         double w;          //物体重量

         double v;           //物体价值

}KNAPNODE;

//堆元素结构体

typedef struct {

         KNAPNODE *p; //结点数据

         double b;          //所指结点的上界

}HEAP;

//比较两个物体价值比

int cmp(OBJECT a,OBJECT b)

{

         return a.v/a.w>b.v/b.w;

}

//交换两个堆元素

void swap(HEAP &a,HEAP &b)

{

         HEAP temp=a;

         a=b;

         b=temp;

}

//堆中元素上移

void sift_up(HEAP H[],int i)

{

         bool done=false;

         if(i!=1)

         {

                  while(!done&&i!=1)

                  {

                           if(H[i].b>H[i/2].b)

                                    swap(H[i],H[i/2]);

                           else

                                    done=true;

                           i=i/2;

                  }

         }

}

//堆中元素下移

void sift_down(HEAP H[],int n,int i)

{

         bool done=false;

         if((2*i)<=n)

         {

                  while(!done&&((i=2*i)<=n))

                  {

                           if(i+1<=n&&H[i+1].b>H[i].b)

                                    i++;

                           if(H[i/2].b<H[i].b)

                                    swap(H[i/2],H[i]);

                           else

                                    done=true;

                  }

         }

}

//往堆中插入结点

void insert(HEAP H[],HEAP x,int &n)

{

         n++;

         H[n]=x;

         sift_up(H,n);

}

//删除堆中的结点

void del(HEAP H[],int &n,int i)

{

         HEAP x,y;

         x=H[i];

         y=H[n];

         n--;

         if(i<=n)

         {

                  H[i]=y;

                  if(y.b>=x.b)

                           sift_up(H,i);

                  else

                           sift_down(H,n,i);

         }

}

//获得堆顶元素并删除

HEAP delete_max(HEAP H[],int &n)

{

         HEAP x=H[1];

         del(H,n,1);

         return x;

}

//计算分支结点的上界

void bound(KNAPNODE* node,double M,OBJECT ob[],int n)

{

         int i=node->k;

         double w=node->w;

         double v=node->v;

         if(node->w>M)

         {                                                   //物体重量超过背包的容量

                  node->b=0;                         //上界置为0

         }

         else

         {

                  while((w+ob[i].w<=M)&&(i<n))

                  {

                           w+=ob[i].w;                //计算背包已载入载重

                           v+=ob[i++].v;    //计算背包已装入价值

                  }

                  if(i<n)

                           node->b=v+(M-w)*ob[i].v/ob[i].w;

                  else

                           node->b=v;

         }

}

//分支限界求解函数

//输入:n个物体的重量和价值数组ob[],背包载重M

//输出:装入背包的物体最优价值v

double knapsack_bound(OBJECT ob[],double M,int n)

{

         int i,k=0;                              //堆中元素个数计数器初始化为0

         double v;

         KNAPNODE *xnode,*ynode,*znode;

         HEAP x,y,z,*heap;

         heap=new HEAP[n*n];               //分配堆的存储空间

         sort(ob,ob+n,cmp);            //对物体按照价值重量比排序

         xnode=new KNAPNODE;            //建立父结点

         for(i=0;i<n;i++)          //初始化结点

                  xnode->s1[i]=false;

         xnode->k=xnode->w=xnode->v=0;

         while (xnode->k<n)

         {

                  ynode=new KNAPNODE;   //建立y结点

                  *ynode=*xnode;               //结点x的数据复制到y

                  ynode->s1[ynode->k]=true;      //装入第k个物体

                  ynode->w+=ob[ynode->k].w;    //背包中物体的累计重量

                  ynode->v+=ob[ynode->k].v;      //背包中物体的累计价值

                  ynode->k++;                                         //搜索深度++

                  bound(ynode,M,ob,n);               //计算结点y的上界

                  y.b=ynode->b;

                  y.p=ynode;

                  insert(heap,y,k);                          //结点y按上界的值插入到堆中

                  znode=new KNAPNODE;                     //建立结点z

                  *znode=*xnode;                                  //结点x数据复制到z

                  znode->k++;                                         //搜索深度++

                  bound(znode,M,ob,n);               //计算结点z的上界

                  z.b=znode->b;

                  z.p=znode;

                  insert(heap,z,k);                          //结点z按上界的值插入堆中

                  delete xnode;

                  x=delete_max(heap,k);              //获得堆顶元素作为新的父亲结点

                  xnode=x.p;

         }

         v=xnode->b;

         delete xnode;

         delete heap;

         return v;                                                //返回背包中物体的价值

}

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

double KnapSack(int n,int c,OBJECT ob[])

{

         int i,j;

         vector<vector<double> > V(n+1, vector<double>(c+1));

         for(i=0;i<=n;i++)

                  V[i][0]=0;

         for(j=0;j<=c;j++)

                  V[0][j]=0;

         for(i=1;i<=n;i++)

         {

                  for(j=1;j<=c;j++)

                  {

                            if(j<ob[i].w)

                                    V[i][j]=V[i-1][j];

                           else

                                    V[i][j]=max(V[i-1][j],V[i-1][j-ob[i].w]+ob[i].v);

/*                       cout<<V[i][j]<<" ";*/

                  }

/*              cout<<endl;*/ 

         }

         return V[n][c];

}

//蛮力法---------------------------------------

void conversion(int n,int b[MAX])//将n化为二进制形式,结果存放到数组b中

{

    int i;

    for(i=0;i<MAX;i++)

         {

                  b[i] = n%2;

                  n = n/2;

                  if(n==0)break;

         }

}

double Force(int n,int c,OBJECT ob[])

{

         int i,j;

         int b[MAX];

         double maxv;

         double temp_v,temp_w;

         maxv = 0;

         //分别求出所有的子集,按要求寻找最大的子集

    for (i=0;i<pow(2,n);i++)

         {

        for (j=0;j<n;j++)

                  {

                           b[j] = 0;

                  }

        conversion(i,b);

        temp_v = 0;

        temp_w = 0;

                  for (j=0;j<n;j++)

                  {

                           if (b[j]==1)

                           {

                                    temp_w = temp_w+ob[j].w;

                                    temp_v = temp_v+ob[j].v;

                           }

                  }

                  if ((temp_w<=c)&&(temp_v>=maxv))

                           maxv = temp_v;        

         }

         return maxv;

}

//主函数===============================================

void main()

{       

         OBJECT t[MAX];

         int n;                                    //实际物品数

         int c;                            //背包容量

         int i;

         //测时间

         LARGE_INTEGER begin,end,frequency;

         QueryPerformanceFrequency(&frequency);

         //输入

         cout<<"输入物品总数:";

         cin>>n;

         cout<<"输入背包的总容量:";

         cin>>c;

         srand(time(0));

         for (i=0;i<n;i++)

         {

                  t[i].w=rand()%11+rand()%17;

                  t[i].v=rand()%37+rand()%61;

                  cout<<"第"<<i+1<<"个物品重量:"<<t[i].w

                           <<" 价值:"<<t[i].v<<endl;

         }

        

         //分支限界-----------------------------------------------------

         double maxV;

         QueryPerformanceCounter(&begin);

         maxV=knapsack_bound(t,c,n);

         QueryPerformanceCounter(&end);

         cout<<"**************分支限界法********************"<<endl;

         cout<<"结果:"<<maxV<<endl;

         cout<<"时间:"

                  <<(double)(end.QuadPart-begin.QuadPart)/frequency.QuadPart

                  <<"s"<<endl;

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

         QueryPerformanceCounter(&begin);

         maxV=KnapSack(n,c,t);

         QueryPerformanceCounter(&end);

         cout<<"**************动态规划法********************"<<endl;

         cout<<"结果:"<<maxV<<endl;

         cout<<"时间:"

                  <<(double)(end.QuadPart-begin.QuadPart)/frequency.QuadPart

                  <<"s"<<endl;

         //蛮力法-------------------------------------------------------

         QueryPerformanceCounter(&begin);

         maxV=Force(n,c,t);

         QueryPerformanceCounter(&end);

         cout<<"****************蛮力法**********************"<<endl;

         cout<<"结果:"<<maxV<<endl;

         cout<<"时间:"

                  <<(double)(end.QuadPart-begin.QuadPart)/frequency.QuadPart

                  <<"s"<<endl;

         //回溯法------------------------------------------------------

         QueryPerformanceCounter(&begin);

         sort(t,t+n,cmp);

         BackTrack(0,n,c,t);

         QueryPerformanceCounter(&end);

         cout<<"****************回溯法**********************"<<endl;

         cout<<"结果:"<<bestV<<endl;

         cout<<"时间:"

                  <<(double)(end.QuadPart-begin.QuadPart)/frequency.QuadPart

                  <<"s"<<endl;

}

四、运行输出结果:

   

五、调试和运行程序过程中产生的问题、采取的措施及获得的相关经验教训:

    该实验用四种方法求解0-1背包问题,蛮力法,回溯法,分支限界法的复杂度都是指数级,动态规划法结果错误,分支限界方法是从网上找的。

更多相关推荐:
实验报告 范本

研究生实验报告范本实验课程实验名称实验地点学生姓名学号指导教师范本实验时间年月日一实验目的熟悉电阻型气体传感器结构及工作原理进行基于聚苯胺敏感薄膜的气体传感器的结构设计材料制作材料表征探测单元制作与测试实验结果...

实验报告范本

学生实验报告书实验课程名称开课学院指导教师姓名学生姓名学生专业班级200200学年第学期实验教学管理基本规范实验是培养学生动手能力分析解决问题能力的重要环节实验报告是反映实验教学水平与质量的重要依据为加强实验过...

实验报告范本

AMT执行机构实验报告实验对象NJ7150变速箱总成实验内容第四代选换档执行机构高低温实验报告人审核批准报告时间20xx苏州绿控传动科技有限公司第四代选换档执行机构高低温试验报告一实验装置零部件清单二已填写完整...

实验报告范本

实验报告范本,内容附图。

实验报告范本

开放实验室报告1234

实验报告范本

学生实验报告书实验课程名称开课学院指导教师姓名学生姓名学生专业班级200200学年第学期实验教学管理基本规范实验是培养学生动手能力分析解决问题能力的重要环节实验报告是反映实验教学水平与质量的重要依据为加强实验过...

实验报告范例

Word排版示例22实验目的1掌握资源管理器和我的电脑的基本操作2掌握文件和文件夹的浏览选择操作3掌握文件和文件夹的新建复制移动删除操作4掌握文件和文件夹的查找操作实验内容1资源管理器的操作2文件和文件夹的操作...

实验报告样本

深圳大学实验报告课程名称学院实验时间实验报告提交时间教务部制注1报告内的项目或内容设置可根据实际情况加以调整和补充2教师批改学生实验报告时间应在学生提交实验报告时间后10日内

实验报告要求及范例2

矿井井巷模型观摩演示实验报告现代化矿井模拟系统学生姓名张彬学号31120xx10423专业班级安全工程122班课程名称矿井开采实验教师高保彬上课日期20xx年11月30日安全科学与工程学院安全工程系20xx年1...

实验室资质认定管理评审报告

管理评审报告管理评审的目的:对公司的质量体系的适宜性、充分性、有效性进行评审,确保质量体系满足实验室资质认定评审准则的要求,使实验室的管理体系不断完善与改进。管理评审的范围:公司各部门管理评审的内容:质量方针、…

实验室认证管理评审报告模拟

泉州市惠信建设工程质量检测有限公司20xx年管理评审报告一、评审目的:验证本公司质量方针、目标和质量管理体系的适应性、充分性和有效性,包括评价质理管理体系改进的机会和变更的需要。二、评审范围:本公司质量方针、目…

实验室管理评审报告

深圳培训网质量管理体系评审深圳培训网质量管理体系评审1质量管理体系评审是组织的最高管理者就质量方针和质量目标有规则的系统的评价质量管理体系的适宜性充分性有效性和效率2评价可包括考虑修改质量方针和质量目标的需要以...

实验报告范本(52篇)