最新文章专题视频专题问答1问答10问答100问答1000问答2000关键字专题1关键字专题50关键字专题500关键字专题1500TAG最新视频文章推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37视频文章20视频文章30视频文章40视频文章50视频文章60 视频文章70视频文章80视频文章90视频文章100视频文章120视频文章140 视频2关键字专题关键字专题tag2tag3文章专题文章专题2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章专题3
当前位置: 首页 - 正文

算法设计与分析实验报告资料

来源:动视网 责编:小OO 时间:2025-10-02 15:32:58
文档

算法设计与分析实验报告资料

算法设计与分析实验报告学院:信息学院专业:物联网1101姓名:***学号:********2013年11月作业10-1背包问题的动态规划算法1.1算法应用背景从计算复杂性来看,背包问题是一个NP难解问题。半个世纪以来,该问题一直是算法与复杂性研究的热点之一。另外,背包问题在信息加密、预算控制、项目选择、材料切割、货物装载、网络信息安全等应用中具有重要的价值。如果能够解决这个问题那么则具有很高的经济价值和决策价值,在上述领域可以获得最大的价值。本文从动态规划角度给出一种解决背包问题的算法。1.2
推荐度:
导读算法设计与分析实验报告学院:信息学院专业:物联网1101姓名:***学号:********2013年11月作业10-1背包问题的动态规划算法1.1算法应用背景从计算复杂性来看,背包问题是一个NP难解问题。半个世纪以来,该问题一直是算法与复杂性研究的热点之一。另外,背包问题在信息加密、预算控制、项目选择、材料切割、货物装载、网络信息安全等应用中具有重要的价值。如果能够解决这个问题那么则具有很高的经济价值和决策价值,在上述领域可以获得最大的价值。本文从动态规划角度给出一种解决背包问题的算法。1.2
算法设计与分析实验报告

    

                                         学院:信息学院

专业:物联网1101

                                       姓名:***

                                         学号:********

 

2013年11月

作业1 0-1背包问题的动态规划算法

1.1算法应用背景

从计算复杂性来看,背包问题是一个NP难解问题。半个世纪以来,该问题一直是算法与复杂性研究的热点之一。另外,背包问题在信息加密、预算控制、项目选择、材料切割、货物装载、网络信息安全等应用中具有重要的价值。如果能够解决这个问题那么则具有很高的经济价值和决策价值,在上述领域可以获得最大的价值。本文从动态规划角度给出一种解决背包问题的算法。

1.2算法原理 

1.2.1、问题描述:

给定n种物品和一背包。物品i的重量是wi,其价值为vi,背包的容量为C。问:应如何选择装入背包的物品,使得装入背包中物品的总价值最大?

形式化描述:给定c >0, wi >0, vi >0 , 1≤i≤n.要求找一n元向量(x1,x2,…,xn,), xi∈{0,1}, ∋ ∑ wi xi≤c,且∑ vi xi达最大.即一个特殊的整数规划问题。

1.2.2、最优性原理:

设(y1,y2,…,yn)是 (3.4.1)的一个最优解.则(y2,…,yn)是下面相应子问题的一个最优解:

证明:使用反证法。若不然,设(z2,z3,…,zn)是上述子问题的一个最优解,而(y2,y3,…,yn)不是它的最优解。显然有

∑vizi > ∑viyi (i=2,…,n)

且 w1y1+ ∑wizi<= c

因此 v1y1+ ∑vizi (i=2,…,n) > ∑ viyi, (i=1,…,n)

 说明(y1,z2, z3,…,zn)是(3.4.1)0-1背包问题的一个更优解,导出(y1,y2,…,yn)不是背包问题的最优解,矛盾。

1.2.3、递推关系:

设所给0-1背包问题的子问题

的最优值为m(i,j),即m(i,j)是背包容量为j,可选择物品为i,i+1,…,n时0-1背包问题的最优值。由0-1背包问题的最优子结构性质,可以建立计算m(i,j)的递归式:

注:(3.4.3)式此时背包容量为j,可选择物品为i。此时在对xi作出决策之后,问题处于两种状态之一:

 (1)背包剩余容量是j,没产生任何效益;

 (2)剩余容量j-wi,效益值增长了vi ;

1.3算法描述

int m[100][100];//前i个物品装入容量为j的背包中获得的最大价值

 int s;//获得的最大价值

 int w[15];//物品的重量

 int v[15];//物品的价值

 int x[15];//物品的选取状态,1表示被选中 0表示未选中

 int n,i;

 int c;//背包最大容量

int max(int a,int b)//获得最大值

int min(int a,int b)//获得最小值

void KnapSack(int n,int w[],int v[],int c)//背包问题主算法

先为m[n][j] 初始化初值然后根据递归方程式进行穷举递归直到 m[1][c], m[1][c] 即为所获得的最大价值。

void Traceback(int n,int w[],int x[],int c)//回溯算法,依次标注被选中的物品

通过一个循环过程检验装入第i个物品与装入i+1个物品的价值如果相同,则x[i]=0。

1.4程序实现及程序截图

1.4.1程序源码

#include

using namespace std;

int m[100][100];//前i个物品装入容量为j的背包中获得的最大价值

int max(int a,int b)

{

if(a>=b)

       return a;

   else return b;

}

int min(int a,int b)

{

if(a>=b)

       return b;

   else return a;

}

void KnapSack(int n,int w[],int v[],int c)

{

   int i,j;

   int jMax=min(w[n]-1,c);

for(j=0;j<=jMax;j++) m[n][j]=0;

for(j=w[n];j<=c;j++) m[n][j]=v[n];

for(i=n-1;i>1;i--)

   {

       jMax=min(w[i]-1,c);

for(j=0;j<=jMax;j++) m[i][j]=m[i+1][j];

for(j=w[i];j   }

   m[1][c]=m[2][c];

if(c>=w[1]) m[1][c]=max(m[1][c],m[2][c-w[1]]+v[1]);

}

void Traceback(int n,int w[],int x[],int c)

{

    int i;

for(i=1;i     if(m[i][c]==m[i+1][c]) x[i]=0;

     else{x[i]=1;c-=w[i];}

     x[n]=(m[n][c])?1:0;

}

int main()

{

    int s;//获得的最大价值

    int w[15];//物品的重量

    int v[15];//物品的价值

    int x[15];//物品的选取状态

    int n,i;

    int c;//背包最大容量

cout <<"请输入背包的最大容量:"<< endl;

cin>>c;

cout<<"输入物品数:\\n"< cin>>n;

cout<<"请分别输入物品的重量:"< for(i=1;i<=n;i++)

cin>>w[i];

cout<<"请分别输入物品的价值:"< for(i=1;i<=n;i++)

cin>>v[i];

    KnapSack(n,w,v,c);

    Traceback(n,w,x,c);

    s=m[1][c];

cout<<"最大物品价值为:"< cout< cout<<"选中的物品为:"< for(i=1;i<=n;i++)

cout<return 0;

1.4.2程序截图

1.5学习或程序调试心得

利用动态规划求解0-1背包问题的复杂度为0(min{nc,2n}。动态规划主要是求解最优决策序列,当最优决策序列中包含最优决策子序列时,可建立动态规划递归方程,它可以帮助高效地解决问题。

作业2  0-1背包问题的回溯算法

1.1算法应用背景

背包问题是一个在运筹学领域里常见的典型NP-C难题,也是算法设计分析中的经典问题,对该问题的求解方法的研究无论是在理论上,还是在实践中都具有重要意义。对这个问题的求解已经研究出了不少的经典方法,对该问题的探索和应用研究一直在进行。在先进理论指导下,求解0-1背包问题具有科学、高效、经济、灵活、方便等显著特点。

那么要解决背包问题,首要的前提就是设计出好的算法,想求得背包问题的解,就要先设计出算法,本文采用回溯法对背包问题给出具体算法设计和实现过程。如何将背包问题应用于实际问题中,有针对性地设计适合求解实际0-1背包问题的算法,并很好地解决实际问题,是计算机工作者不断思索、研究的一个领域。

2.2算法原理 

2.2.1    问题描述

问题的一般描述是:旅行者背包登山,背包的最大承重为M,现有n个物品可供选择装入背包,第i个物品莺量为wi,价值为pi,假定物品i的一部分xi(0xi1)放人背包,获得价值为xipi,由于背包最大承重为M,要求装入物品总质量不过超过M,问旅行者应该如何选择物品装入背包,使得装入物品的价值总和达到最大值。

背包问题的数学描述如下:要求找到一个n元向量(x1,x2…xn),在满足约束条件:情况下,使得目标函数,其中,1in;M>0;wi>0;pi>0。满足约束条件的任何向量都是一个可行解,而使得目标函数达到最大的那个可行解则为最优解。

给定n 种物品和1个背包。物品i 的重量是wi,其价值为pi,背包的容量为M。问应如何装入背包中的物品,使得装人背包中物品的总价值最大?在选择装人背包的物品时,对每种物品i只有两种选择,即装入背包、不装入背包。不能将物品i 装人背包多次,也不能只装入部分的物品i。该问题称为0-1背包问题。

0-1背包问题的符号化表示是,给定M>0, w i >0, pi >0,1in ,要求找到一个n元0-1向量向量(x1,x2…xn), X i =0 或1 , 1in, 使得

 ,而且达到最大。

2.2.2算法分析

1、问题的解空间:应用回溯法解问题时,首先应明确定义问题的解空间。问题的解空间应到少包含问题的一个(最优)解。

2、回溯法的基本思想:确定了解空间的组织结构后,回溯法就从开始结点(根结点)出发,以深度优先的方式搜索整个解空间。这个开始结点就成为一个活结点,同时也成为当前的扩展结点。在当前的扩展结点处,搜索向纵深方向移至一个新结点。这个新结点就成为一个新的活结点,并成为当前扩展结点。如果在当前的扩展结点处不能再向纵深方向移动,则当前扩展结点就成为死结点。换句话说,这个结点不再是一个活结点。此时,应往回移动(回溯)至最近的一个活结点处,并使这个活结点成为当前的扩展结点。回溯法即以这种工作方式递归地在解空间中搜索,直至找到所要求的解或解空间中已没有活结点时为止。

运用回溯法解题通常包含以下三个步骤:

(1)针对所给问题,定义问题的解空间;

(2)确定易于搜索的解空间结构;

(3)以深度优先的方式搜索解空间,并且在搜索过程中用剪枝函数避免无效搜索;

3、递归回溯:由于回溯法是对解空间的深度优先搜索,因此在一般情况下可用递归函数来实现回溯法。问题的一般描述是:旅行者背包登山,背包的最大承重为M,现有n个物品可供选择装入背包,第i个物品莺量为wi,价值为pi,假定物品i的一部分xi(0xi1)放人背包,获得价值为xipi,由于背包最大承重为M,要求装入物品总质量不过超过M,问旅行者应该如何选择物品装入背包,使得装入物品的价值总和达到最大值。

背包问题的数学描述如下:要求找到一个n元向量(x1,x2…xn),在满足约束条件:情况下,使得目标函数,其中,1in;M>0;wi>0;pi>0。满足约束条件的任何向量都是一个可行解,而使得目标函数达到最大的那个可行解则为最优解。

给定n 种物品和1个背包。物品i 的重量是wi,其价值为pi,背包的容量为M。问应如何装入背包中的物品,使得装人背包中物品的总价值最大?在选择装人背包的物品时,对每种物品i只有两种选择,即装入背包、不装入背包。不能将物品i 装人背包多次,也不能只装入部分的物品i。该问题称为0-1背包问题。

0-1背包问题的符号化表示是,给定M>0, w i >0, pi >0,1in ,要求找到一个n元0-1向量向量(x1,x2…xn), X i =0 或1 , 1in, 使得

 ,而且达到最大。

1、问题的解空间:应用回溯法解问题时,首先应明确定义问题的解空间。问题的解空间应到少包含问题的一个(最优)解。

2、回溯法的基本思想:确定了解空间的组织结构后,回溯法就从开始结点(根结点)出发,以深度优先的方式搜索整个解空间。这个开始结点就成为一个活结点,同时也成为当前的扩展结点。在当前的扩展结点处,搜索向纵深方向移至一个新结点。这个新结点就成为一个新的活结点,并成为当前扩展结点。如果在当前的扩展结点处不能再向纵深方向移动,则当前扩展结点就成为死结点。换句话说,这个结点不再是一个活结点。此时,应往回移动(回溯)至最近的一个活结点处,并使这个活结点成为当前的扩展结点。回溯法即以这种工作方式递归地在解空间中搜索,直至找到所要求的解或解空间中已没有活结点时为止。

运用回溯法解题通常包含以下三个步骤:

(1)针对所给问题,定义问题的解空间;

(2)确定易于搜索的解空间结构;

(3)以深度优先的方式搜索解空间,并且在搜索过程中用剪枝函数避免无效搜索;

3、递归回溯:由于回溯法是对解空间的深度优先搜索,因此在一般情况下可用递归函数来实现回溯法。

   回溯法是一种系统地搜索问题解答的方法。为了实现回溯,首先需要为问题定义一个解空间,这个解空间必须至少包含问题的一个解(可能是最优的)。一旦定义了解空间的组织方要选择一个对象的子集,将它们装人背包,以便获得的收益最大,则解空间应组织成子集树的形状。首先形成一个递归算法,去找到可获得的最大收益。然后,对该算法加以改进,形成代码。改进后的代码可找到获得最大收益时包含在背包中的对象的集合。

左子树表示一个可行的结点,无论何时都要移动到它,当右子树可能含有比当前最优解还优的解时,移动到它。一种决定是否要移动到右子树的简单方法是r为还未遍历的对象的收益之和,将r加到cp (当前节点所获收益)之上,若( r+cp) bestp(目前最优解的收益),则不需搜索右子树。一种更有效的方法是按收益密度vi/wi对剩余对象排序,将对象按密度递减的顺序去填充背包的剩余容量, 当遇到第一个不能全部放人背包的对象时, 就使用它的一部分。

2.3算法描述

概要设计

0—1背包问题是一个子集选取问题,适合于用子集树表示0—1背包问题的解空间。在搜索解空间树是,只要其左儿子节点是一个可行结点,搜索就进入左子树,在右子树中有可能包含最优解是才进入右子树搜索。否则将右子树剪去。

int c;//背包容量

int n; //物品数

int *w;//物品重量数组

int *p;//物品价值数组

int cw;//当前重量

int cp;//当前价值

int bestp;//当前最优值

int *bestx;//当前最优解

int *x;//当前解

int Knap::Bound(int i)//计算上界

void Knap::Backtrack(int i)//回溯

int Knapsack(int p[],int w[],int c,int n) //为Knap::Backtrack初始化

 

2.4程序实现及程序截图

2.4.1程序源码

#include

using namespace std; 

class Knap 

friend int Knapsack(int p[],int w[],int c,int n ); 

public: 

void print() 

    for(int m=1;m<=n;m++)

   { 

    cout<   } 

   cout<}; 

private: 

int Bound(int i); 

void Backtrack(int i); 

int c;//背包容量 

int n; //物品数 

int *w;//物品重量数组 

int *p;//物品价值数组 

int cw;//当前重量 

int cp;//当前价值 

int bestp;//当前最优值 

int *bestx;//当前最优解 

int *x;//当前解 

}; 

int Knap::Bound(int i) 

int cleft=c-cw;//剩余容量 

int b=cp; 

while(i<=n&&w[i]<=cleft)

   cleft-=w[i]; 

   b+=p[i]; 

   i++; 

if(i<=n)

   b+=p[i]/w[i]*cleft; 

return b; 

void Knap::Backtrack(int i) 

if(i>n)

    if(bestp{     

    for(int j=1;j<=n;j++)

     bestx[j]=x[j]; 

     bestp=cp; 

return; 

if(cw+w[i]<=c) //搜索左子树 

{             

      x[i]=1; 

   cw+=w[i]; 

   cp+=p[i]; 

   Backtrack(i+1); 

   cw-=w[i]; 

   cp-=p[i]; 

   if(Bound(i+1)>bestp)//搜索右子树 

   { 

       x[i]=0; 

    Backtrack(i+1); 

   } 

class Object 

friend int Knapsack(int p[],int w[],int c,int n); 

public: 

int operator<=(Object a)const

return (d>=a.d);

private: 

int ID; 

float d; 

}; 

int Knapsack(int p[],int w[],int c,int n) 

//为Knap::Backtrack初始化 

int W=0; 

int P=0; 

int i=1; 

Object *Q=new Object[n]; 

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

Q[i-1].ID=i; 

Q[i-1].d=1.0*p[i]/w[i]; 

P+=p[i]; 

W+=w[i]; 

if(W<=c)

   return P;//装入所有物品  

float f; 

for( i=0;ifor(int j=i;j

   if(Q[i].d   { 

     f=Q[i].d; 

   Q[i].d=Q[j].d; 

   Q[j].d=f; 

   } 

Knap K; 

K.p = new int[n+1]; 

    K.w = new int[n+1]; 

K.x = new int[n+1]; 

K.bestx = new int[n+1]; 

K.x[0]=0; 

K.bestx[0]=0; 

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

K.p[i]=p[Q[i-1].ID]; 

K.w[i]=w[Q[i-1].ID]; 

K.cp=0; 

K.cw=0; 

K.c=c; 

K.n=n; 

K.bestp=0; 

//回溯搜索 

K.Backtrack(1); 

    K.print(); 

    delete [] Q; 

delete [] K.w; 

delete [] K.p; 

return K.bestp; 

void main() 

int *p; 

int *w; 

    int c=0; 

int n=0; 

int i=0; 

char k;  

while(k) 

cout<<"请输入背包容量(c):"<cin>>c;

cout<<"请输入物品的个数(n):"<    cin>>n;

p=new int[n+1]; 

w=new int[n+1]; 

p[0]=0; 

w[0]=0; 

cout<<"请输入物品的价值(p):"<for(i=1;i<=n;i++)

   cin>>p[i];

cout<<"请输入物品的重量(w):"<for(i=1;i<=n;i++)

   cin>>w[i];

cout<<"最优解为(bestx):"<cout<<"最优值为(bestp):"<cout<    cout<<"[s] 重新开始"<cout<<"[q] 退出"<cin>>k;

}

2.4.2程序截图

2.5学习或程序调试心得

回溯算法的运行时间取决于它在搜索过程中所生成的结点数,而限界函数可以大量减少所生成的结点个数,省去许多无谓的搜索, 使得搜索速度更快 ,其调用限界函数计算上界需花费O(n)时间 ,最坏情况下有O()个结点需调用限界函数 ,需花费O(n)时间,所以该算法的时间复杂度为O(n)。

 回溯法的另一个重要特性就是在搜索执行的同时产生解空间 在搜索期间的任何时刻 仅保留从开始结点到当前可扩展结点的路径其空间需求为O(从开始结点起最长路径的长度),所以 ,此处该算法的空间复杂度为O(n),回溯法是算法设计的基本方法之一 ,它适用于解一些涉及到寻找一组解的问题或者求满足某些约束条件的最优解的问题,且适用于求解组合数量较大的问题。

作业3 循环赛日程表的分治算法

分治法是一个比较典型也很常见的计算机算法,它不仅可以用来设计各种算法,而且在其他方面也有广泛应用。例如可以用分治思想来构造电路进行数学证明等。设计循环赛日程表即是分治策略的一个具体应用。

3.2.1问题描述:

设有n=2^k个运动员要进行网球循环赛。现要设计一个满足以下要求的比赛日程表:

(1)每个选手必须与其他n-1个选手各赛一次;

 (2)每个选手一天只能参赛一次;

 (3)循环赛在n-1天内结束。

请按此要求将比赛日程表设计成有n行和n-1列的一个表。在表中的第i行,第j列处填入第i个选手在第j天所遇到的选手。其中1≤i≤n,1≤j≤n-1。8个选手的比赛日程表如下图:

3.2.2算法思路:

按分治策略,我们可以将所有的选手分为两半,则n个选手的比赛日程表可以通过n/2个选手的比赛日程表来决定。递归地用这种一分为二的策略对选手进行划分,直到只剩下两个选手时,比赛日程表的制定就变得很简单。这时只要让这两个选手进行比赛就可以了。如上图,所列出的正方形表是8个选手的比赛日程表。其中左上角与左下角的两小块分别为选手1至选手4和选手5至选手8前3天的比赛日程。据此,将左上角小块中的所有数字按其相对位置抄到右下角,又将左下角小块中的所有数字按其相对位置抄到右上角,这样我们就分别安排好了选手1至选手4和选手5至选手8在后4天的比赛日程。依此思想容易将这个比赛程表推广到具有任意多个选手的情形。

(1)用一个for循环输出日程表的第一行 for(int i=1;i<=N;i++) a[1][i] = i

(2)然后定义一个m值,m初始化为1,m用来控制每一次填充表格时i(i表示行)和j(j表示列)的起始填充位置。

(3)用一个for循环将问题分成几部分,对于k=3,n=8,将问题分成3大部分,第一部分为,根据已经填充的第一行,填写第二行,第二部分为,根据已经填充好的第一部分,填写第三四行,第三部分为,根据已经填充好的前四行,填写最后四行。for (ints=1;s<=k;s++) N/=2;

(4)用一个for循环对③中提到的每一部分进行划分for(intt=1;t<=N;t++)对于第一部分,将其划分为四个小的单元,即对第二行进行如下划分

同理,对第二部分(即三四行),划分为两部分,第三部分同理。

 (5)最后,根据以上for循环对整体的划分和分治法的思想,进行每一个单元格的填充。填充原则是:对角线填充for(int i=m+1;i<=2*m;i++) //i控制行

for(int j=m+1;j<=2*m;j++) //j控制列

{

a[i][j+(t-1)*m*2]= a[i-m][j+(t-1)*m*2-m];/*右下角的值等于左上角的值 */

a[i][j+(t-1)*m*2-m] =a[i-m][j+(t-1)*m*2];/*左下角的值等于右上角的值 */

 } 

运行过程:

(1)由初始化的第一行填充第二行

(2)由s控制的第一部分填完。然后是s++,进行第二部分的填充

(3)最后是第三部分的填充

#include

#include

using namespace std;

void Table(int k,int n,int **a);

void input(int &k);

void output(int **a,int n);

int main()

{

    int k;

    input(k);

    int n=1;

//n=2k(k>=1)个选手参加比赛

for(int i=1; i<=k; i++)

        n *= 2;

    //根据n动态分配二维数组a

    int **a = new int *[n+1];

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

    {

        a[i] = new int[n+1];

    }

    Table(k,n,a);

cout<<"循环赛事日程表为:"<    output(a,n);

    //释放空间

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

    {

        delete[] a[i];

    }

    delete[] a;

    return 0;

}

void input(int &k)

{

cout<<"请输入k值:"< cin>>k;

}

void output(int **a,int n)

{

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

    {

for(int j=1; j<=n; j++)

        {

cout<        }

cout<    }

}

void Table(int k,int n,int **a)

{

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

        a[1][i]=i;//设置日程表第一行

    int m = 1;//每次填充时,起始填充位置

for(int s=1; s<=k; s++)

    {

        n /= 2;

for(int t=1; t<=n; t++)

        {

for(int i=m+1; i<=2*m; i++)//控制行

            {

for(int j=m+1; j<=2*m; j++)//控制列

                {

                    a[i][j+(t-1)*m*2] = a[i-m][j+(t-1)*m*2-m];//右下角等于左上角的值

                    a[i][j+(t-1)*m*2-m] = a[i-m][j+(t-1)*m*2];//左下角等于右上角的值

                }

            }

        }

        m *= 2;

    }

}

按分治策略,我们可以将所有的选手分为两半,则n个选手的比赛日程表可以通过n/2个选手的比赛日程表来决定。递归地用这种一分为二的策略对选手进行划分,直到只剩下两个选手时,比赛日程表的制定就变得很简单。这时只要让这两个选手进行比赛就可以了。

分治法解决问题的关键在于将大的问题转化为小的问题,是分而治之的思想。而且分成的小问题往往与原问题相同,用递归的方法解决子问题。用递归方法编写的问题解决程序具有结构清晰,可读性强等优点,且递归算法的设计比非递归算法的设计往往要容易一些,所以当问题本身是递归定义的,或者问题所涉及到的数据结构是递归定义的,或者是问题的解决方法是递归形式的时候,往往采用递归算法来解决。递归可以说是一种通用之法。

作业4 活动安排的贪心算法

4.1算法应用背景

在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,他所做出的仅是在某种意义上的局部最优解。贪心算法不是对所有问题都能得到整体最优解,但对范围相当广泛的许多问题他能产生整体最优解或者是整体最优解的近似解。活动安排是可以用贪心算法求解的很好例子。该问题要求高效的安排一系列争用某一公共资源的活动。贪心算法提供了一个简单、漂亮的方法,是尽可能多的活动能兼容地使用同一公共资源。

4.2算法原理 

4.2.1 问题描述:

设有n个活动的集合E={1,2,…,n},其中每个活动都要求使用同一资源,如演讲会场等,而在同一时间内只有一个活动能使用这一资源。每个活动i都有一个要求使用该资源的起始时间si和一个结束时间fi,且si 4.2.2算法思路:

贪心算法采用自顶向下,以迭代的方法做出相继的贪心选择,每做一次贪心选择就将所求问题简化为一个规模更小的子问题,通过每一步贪心选择,可得到问题的一个最优解,虽然每一步上都要保证能获得局部最优解,但由此产生的全局解有时不一定是最优的,所以贪婪法不要回溯。所谓贪心选择性质是指所求问题的整体最优解可以通过一系列局部最优的选择,即贪心选择来达到。这是贪心算法可行的第一个基本要素。贪心算法则通常以自顶向下的方式进行,以迭代的方式作出相继的贪心选择,每作一次贪心选择就将所求问题简化为规模更小的子问题。

4.3算法描述    

将活动按照结束时间进行从小到大排序。然后用i代表第i个活动,s[i]代表第i个活动开始时间,f[i]代表第i个活动的结束时间。按照从小到大排序,挑选出结束时间尽量早的活动,并且满足后一个活动的起始时间晚于前一个活动的结束时间,全部找出这些活动就是最大的相容活动子集合。事实上系统一次检查活动i是否与当前已选择的所有活动相容。若相容活动i加入已选择活动的集合中,否则,不选择活动i,而继续下一活动与集合A中活动的相容性。若活动i与之相容,则i成为最近加入集合A的活动,并取代活动j的位置。

各活动的起始时间和结束时间存储于数组s和f中,且按结束时间的非减序排列。如果所给的活动未按此序排列,可以用O(nlogn)的时间重排。

4.4程序实现及程序截图

4.4.1程序源码

// 活动安排问题 贪心算法

#include

using namespace std;

template

void GreedySelector(int n, Type s[], Type f[], bool A[]);

const int N = 11;

int main()

{

    //下标从1开始,存储活动开始时间

    int s[] = {0,1,3,0,5,3,5,6,8,8,2,12};

    //下标从1开始,存储活动结束时间

    int f[] = {0,4,5,6,7,8,9,10,11,12,13,14};

    bool A[N+1];

cout<<"各活动的开始时间,结束时间分别为:"< for(int i=1;i<=N;i++)

    {

cout<<"["<    }

    GreedySelector(N,s,f,A);

cout<<"最大相容活动子集为:"< for(int i=1;i<=N;i++)

    {

        if(A[i]){

cout<<"["<        }

    }

    return 0;

}

template

void GreedySelector(int n, Type s[], Type f[], bool A[])

{

    A[1]=true;

    int j=1;//记录最近一次加入A中的活动

for (int i=2;i<=n;i++)//依次检查活动i是否与当前已选择的活动相容

    {

if (s[i]>=f[j])

        {

            A[i]=true;

            j=i;

        }

        else

        {

            A[i]=false;

        }

    }

}

4.4.2程序截图

4.5学习或程序调试心得

由于输入的活动以其完成时间的非减序排列,所以算法greedySelector每次总是选择具有最早完成时间的相容活动加入集合A中。直观上,按这种方法选择相容活动为未安排活动留下尽可能多的时间。也就是说,该算法的贪心选择的意义是使剩余的可安排时间段极大化,以便安排尽可能多的相容活动。算法greedySelector的效率极高。当输入的活动已按结束时间的非减序排列,算法只需O(n)的时间安排n个活动,使最多的活动能相容地使用公共资源。如果所给出的活动未按非减序排列,可以用O(nlogn)的时间重排。

生活中其实我们也常常不经意地用到贪心算法,所谓贪心就是值得每步操作实现最优,很多时候个体最优便是整体最优,从而得到最优解。

文档

算法设计与分析实验报告资料

算法设计与分析实验报告学院:信息学院专业:物联网1101姓名:***学号:********2013年11月作业10-1背包问题的动态规划算法1.1算法应用背景从计算复杂性来看,背包问题是一个NP难解问题。半个世纪以来,该问题一直是算法与复杂性研究的热点之一。另外,背包问题在信息加密、预算控制、项目选择、材料切割、货物装载、网络信息安全等应用中具有重要的价值。如果能够解决这个问题那么则具有很高的经济价值和决策价值,在上述领域可以获得最大的价值。本文从动态规划角度给出一种解决背包问题的算法。1.2
推荐度:
  • 热门焦点

最新推荐

猜你喜欢

热门推荐

专题
Top