算法分析实验报告--贪心算法

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

贪心算法

一、  试验名称:贪心算法

(1)    写出源程序,并编译运行

(2)    详细记录程序调试及运行结果

二、实验目的

(1)   了解贪心算法思想

(2)   掌握贪心法典型问题,如背包问题、作业调度问题等。

三、实验内容

(1)   编写一个简单的程序,实现单源最短路径问题。

(2)   编写一段程序,实现找零。

(3)   编写程序实现多机调度问题

四、算法思想分析

(1)   编写一个简单的程序,实现单源最短路径问题。

用P,T分别表示某个点的P标号、T标号,si表示第i步时,具P标号点的集合。为了在求出从vs到各点的距 离的同时,也求出从Vs到各点的最短路,给每个点v以一个λ值,算法终止时λ(v)=m,表示在Vs到v的最短路上,v的前一个点是Vm;如果λ(v)= ∞,表示图G中不含从Vs到v的路;λ(Vs)=0。

(2)   编写一段程序,实现找零。

先举个例子,假如老板要找给我99分钱,他有上面的面值分别为25,10,5,1的硬币数,为了找给我最少的硬币数,那么他是不是该这样找呢,先看看该找多少个25分的, 99/25=3,好像是3个,要是4个的话,我们还得再给老板一个1分的,我不干,那么老板只能给我3个25分的拉,由于还少给我24,所以还得给我2个10分的和4个1分。

(3)   编写程序实现多机调度问题

1、把作业按加工所用的时间从大到小排序

2、如果作业数目比机器的数目少或相等,则直接把作业分配下去

3、如果作业数目比机器的数目多,则每台机器上先分配一个作业,如下的作业分配时,是选那个表头上s最小的链表加入新作业。

五、算法源代码及用户屏幕

(1)   编写一个简单的程序,实现单源最短路径问题。

//Dijkstra算法

#include<iostream>

#include<cstdlib>

using namespace std;

#define VEX  5//定义结点的个数

#define maxpoint 100

double graph[][maxpoint]={

{ 0 , 10 , -1 , 30 , 100 },

 { -1 , 0 , 50 , -1 , -1 } ,

{ -1 , -1 , 0 , -1 , 10 } ,

 { -1 , -1 , 20 , 0 , 60 } ,

 { -1 , -1 , -1 , -1 , 0 }

}; //邻接矩阵

void main()

{int R[maxpoint]={0},B[maxpoint];

int D[VEX],P[VEX];//定义数组D用来存放结点特殊距离,P数组存放父亲结点

//初始时,红点集中仅有源结点0

R[0]=1; B[0]=0;

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

B[i]=1;

//对数组D、P进行初始化 

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

{D[i]=graph[0][i];

 if(D[i]!=-1) P[i]=0;

 else P[i]=-1;

}

//输出邻接矩阵      

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

{for(int j=0;j<VEX;j++)

  cout<<graph[i][j]<<"\t";

  cout<<endl;

}

//输出D、P表头

cout<<"      ";

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

cout<<"D["<<i<<"]   ";

cout<<"   ";

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

cout<<"P["<<i<<"]   ";

cout<<endl;

for(int k=1;k<VEX;k++)  //每次从蓝点集中取出具有最短特殊路长度的结点min

{  for(int min=0;B[min]==0;) min++; //判断j在蓝点集

   for(int j=min;j<VEX;j++)

              if(D[j]!=-1&&D[j]<D[min]&&B[j]==1)  min=j;

     cout<<"min="<<min<<"  ";

 //将具有最短特殊路长度的结点min添加到红点集中

    R[min]=1; B[min]=0;

  //对数组D作必要的修改

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

     if(graph[min][j]!=-1&&min!=j)  //结点min到j间有路

        if(D[j]>D[min]+graph[min][j]||D[j]==-1)

        {D[j]=D[min]+graph[min][j];   //每次迭代求最小值,最后一次即为到源点的最短路径

        P[j]=min;

        }

//输出最短路径

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

cout<<D[i]<<"     ";

cout<<"   ";

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

cout<<P[i]<<"      ";

cout<<endl;

}

}

(2)   编写一段程序,实现找零。

#include<iostream>

#define Max 25

#define Mid 10

#define Next 5

#define Min 1

using namespace std;

    int i = 0;

  int     j = 0;

  int     k = 0;

  int     s = 0;

  int a,b,c;

void changeMoney(int m)

{

 

  if(m >= Max)

  {

            a = m%Max;

            i = m/Max;

            changeMoney(a);

  }

  else if(m >= Mid)

  {

            b = m%Mid;

            j = m/Mid;

            changeMoney(b);

  }

  else if(m >= Next)

  {

            c = m%Next;

            k = m/Next;

            changeMoney(c);

  }

  else if(m >= 1)

  {

            s = m;

  }

}

 

void main()

{

 

  int n;

  cout << "请输入要找零的钱数:"<<endl;

  cin >> n;

  changeMoney(n);

    cout <<"应该找为:"<<endl;

  cout <<i<<" 2角5分  "<<j<<" 1角  "<<k<<" 5分  "<<s<<" 1分  "<<endl;

}

(3)   编写程序实现多机调度问题

#include <iostream>

using namespace std;

#define N 10

#define M 3

void sort(int t[],int n);

int set_work1(int t[],int n);

int max(int t[],int num);

int min(int t[],int m);

int set_work2(int t[],int n);

static int time[N]={2,8,18,32,50,72,98,128,182,200},s[M]={0,0,0};

void main()

{sort(time,N);

  if(M>=N)           //作业数小于机器数

   cout<<set_work1(time,N)<<endl;

 else

  cout<<set_work2(time,N)<<endl;

}

 

void sort(int t[],int n)

{for(int k=0;k<n-1;k++)   //用选择法将处理时间从大到小排序

   {int j=k;

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

        if (t[i]>t[j])   j=i;

  {int temp=t[j];t[j]=t[k];t[k]=temp;}

  }

}

int max(int t[],int num)   //max函数求解处理时间总和最长

 {int max=t[0];

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

    if(max<t[i]) max=t[i];

 return max;

}

int min(int t[],int m)

 {int min=0;    //min记录目前处理作业时间和最小的机器号

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

    if(s[min]>s[i]) min=i;

 return min;

}

int set_work1(int t[],int n)

{ int m=0;

  for(int i=0;i<n;i++)   //分派作业

    s[m++]+=t[i];

  return max(s,N);

}

int set_work2(int t[],int n)

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

   s[min(s,M)]+=t[i];

  return max(s,M);

}

       

六 试验结果及分析

通过这次试验,我深刻认识到自己编码能力的缺失,以后一定多加锻炼。在看懂老师的代码的基础上,自己多加动手,不能仅仅满足于看懂听懂的基础上,只有这样自己才能得到真正的提高。

 

第二篇:算法分析与设计实验报告--贪心法 (2)

            

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

 


实验目的

(1)   了解贪心算法思想

(2)   掌握贪心法典型问题,如背包问题、作业调度问题等。

实验内容

(1)   编写一个简单的程序,实现单源最短路径问题。

(2)   编写一段程序,实现找零。

【问题描述】当前有面值分别为2角5分,1角,5分,1分的硬币,请给出找n分钱的最佳方案(要求找出的硬币数目最少)。

(3)   编写程序实现多机调度问题

【问题描述】要求给出一种作业调度方案,使所给的n个作业在尽可能短的时间内由m台机器加工处理完成。约定,每个作业均可在任何一台机器上加工处理,但未完工前不允许中断处理。作业不能拆分成更小的子作业。

算法思想分析

⑴所谓单源最短路径问题是指:已知图G=(V,E),我们希望找出从某给

定的源结点S∈V到V中的每个结点的最短路径。

首先,我们可以发现有这样一个事实:如果P是G中从vs到vj的最短路,vi是P中的一个点,那么,从vs沿P到vi的路是从vs到vi的最短路。

(一)Dijkstra算法

对于图G,如果所有Wij≥0的情形下,目前公认的最好的方法是由Dijkstra于1959年提出来的。

例1 已知如下图所示的单行线交通网,每弧旁的数字表示通过这条单行线所需要的费用,现在某人要从v1出发,通过这个交通网到v8去,求使总费用最小的旅行路线。

Dijkstra方法的基本思想是从vs出发,逐步地向外探寻最短路。执行过程中,与每个点对应,记录下一个数(称为这个点的标号),它或者表示从vs 到该点的最短路的权(称为P标号)、或者是从vs到该点的最短路的权的上界(称为T标号),方法的每一步是去修改T标号,并且把某一个具T标号的改变为具 P标号的点,从而使G中具P标号的顶点数多一个,这样至多经过n-1(n为图G的顶点数)步,就可以求出从vs到各点的最短路。

在叙述Dijkstra方法的具体步骤之前,以例1为例说明一下这个方法的基本思想。例1中,s=1。因为所有Wij≥0,故有d(v1, v1)=0。这时,v1是具P标号的点。现在考察从v1发出的三条弧,(v1, v2), (v1, v3)和(v1, v4)。如果某人从v1出发沿(v1, v2)到达v2,这时需要d(v1, v1)+w12=6单位的费用;如果他从v1出发沿(v1, v3)到达v3,这时需要d(v1, v1)+w13=3单位的费用;类似地,若沿(v1, v4)到达v4,这时需要d(v1, v1)+w14=1单位的费用。因为min{ d(v1, v1)+w12,d(v1, v1)+w13,d(v1, v1)+w14}= d(v1, v1)+w14=1,可以断言,他从v1到v4所需要的最小费用必定是1单位,即从v1到v4的最短路是(v1, v4),d(v1, v4)=1。这是因为从v1到v4的任一条路P,如果不是(v1, v4),则必是先从v1沿(v1, v2)到达v2,或者沿(v1, v3)到达v3。但如上所说,这时他已需要6单位或3单位的费用,不管他如何再从v2或从v3到达v4,所需要的总费用都不会比1小(因为所有wij≥0)。因而推知d(v1, v4)=1,这样就可以使v4变成具P标号的点。现在考察从v1及v4指向其余点的弧,由上已知,从v1出发,分别沿(v1, v2)、(v1, v3)到达v2, v3,需要的费用分别为6与3,而从v4出发沿(v4, v6)到达v6所需的费用是d(v1, v4)+w46=1+10=11单位。因min{ d(v1, v1)+w12,d(v1, v1)+w13,d(v1, v4)+w46}= d(v1, v1)+w13=3。基于同样的理由可以断言,从v1到v3的最短路是(v1, v3),d(v1, v3)=3。这样又可以使点v3变成具P标号的点,如此重复这个过程,可以求出从v1到任一点的最短路。

在下述的Dijstra方法具体步骤中,用P,T分别表示某个点的P标号、T标号,si表示第i步时,具P标号点的集合。为了在求出从vs到各点的距 离的同时,也求出从Vs到各点的最短路,给每个点v以一个λ值,算法终止时λ(v)=m,表示在Vs到v的最短路上,v的前一个点是Vm;如果λ(v)= ∞,表示图G中不含从Vs到v的路;λ(Vs)=0。

Dijstra方法的具体步骤:

{初始化}

i=0

S0={Vs},P(Vs)=0 λ(Vs)=0

对每一个v<>Vs,令T(v)=+ ∞,λ(v)=+ ∞,

k=s

{开始}

①如果Si=V,算法终止,这时,每个v∈Si,d(Vs,v)=P(v);否则转入②

②考察每个使(Vk,vj)∈E且vj Si的点vj。

如果T(vj)>P(vk)+wkj,则把T(vj)修改为P(vk)+wkj,把λ(vj)修改为k。

③令

如果,则把的标号变为P标号,令 ,k=ji,i=i+1,转①,否则终止,这时对每一个v∈Si,d(vs,v)=P(v),而对每一个。

⑵找零问题

根据常识,我们到店里买东西找钱时,老板总是先给我们最大面值的,要是不够再找面值小一点的,直到找满为止。如果老板都给你找分数的或者几角的,那你肯定不干,另外,他也可能没有那么多零碎的钱给你找。其实这就是一个典型的贪心选择问题。

问题的算法设计与实现:

先举个例子,假如老板要找给我99分钱,他有上面的面值分别为25,10,5,1的硬币数,为了找给我最少的硬币数,那么他是不是该这样找呢,先看看该找多少个25分的, 99/25=3,好像是3个,要是4个的话,我们还得再给老板一个1分的,我不干,那么老板只能给我3个25分的拉,由于还少给我24,所以还得给我2个10分的和4个1分。

⑶多机调度

1、把作业按加工所用的时间从大到小排序

2、如果作业数目比机器的数目少或相等,则直接把作业分配下去

3、 如果作业数目比机器的数目多,则每台机器上先分配一个作业,如下的作业分配时,是选那个表头上s最小的链表加入新作业。

分析与代码

1.单源最短路径

分析:

基本思想:设置顶点集合S并不断地作贪心选择来扩充这个集合。一个顶点属于集合S当且仅当从源到该顶点的最短路径长度已知。

(1)初始时,S中仅含有源节点。

(2)设u是G的某一个顶点,把从源到u且中间只经过S中顶点的路称为从源到u的特殊路径,用数组D[i]记录顶点i当前所对应的最短特殊路径长度。

(3)Dijkstra算法每次从V-S中取出具有最短特殊路长度的顶点u,将u添加到S中,同时对数组D作必要的修改。

(4)一旦S包含了所有V中顶点,dist就记录了从源到所有其它顶点之间的最短路径长度。

代码如下:

#include<iostream>

using namespace std;

int const VEX = 5; //定义结点的个数

int const maxpoint = 15;

int graph[][maxpoint]={

        0 , 10 , -1 , 30 , 100 ,

        -1 , 0 , 50 , -1 , -1  ,

        -1 , -1 , 0 , -1 , 10  ,

        -1 , -1 , 20 , 0 , 60  ,

        -1 , -1 , -1 , -1 , 0

}; //邻接矩阵

int R[maxpoint]={0};

int B[maxpoint];

int D[VEX];

int P[VEX];//定义数组D用来存放结点特殊距离,P数组存放父亲结点

//初始时,红点集中仅有源结点0

void instital(int R[], int B[],int D[], int P[])

{     R[0]=1; B[0]=0;

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

       B[i]=1;

//对数组D、P进行初始化 

       for(int j=0;j<VEX;j++)

       {     D[j]=graph[0][j];

              if(D[j]!=-1) P[j]=0;

              else P[j]=-1;

       }

}

void theshortestway(int R[], int B[],int D[], int P[])

{     for(int k=1;k<VEX;k++)  //每次从蓝点集中取出具有最短特殊路长度的结点min

       {     int min;

              for(min=0;B[min]==0;) min++; //求蓝点集结点最小下标元素

              for(int j=min;j<VEX;j++)

                     if(D[j]!=-1&&D[j]<D[min]&&B[j]==1)  min=j;

//将具有最短特殊路长度的结点min添加到红点集中

              R[min]=1; B[min]=0;

//对数组D作必要的修改

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

                     if(graph[min][j]!=-1&&min!=j)  //结点min到j间有路

                            if(D[j]>D[min]+graph[min][j]||D[j]==-1)

                            {     D[j]=D[min]+graph[min][j];   //每次迭代求最小值,最后一次即为到源点的最短路径

                                   P[j]=min;

                            }

       }

}

void main()

{instital(R, B, D, P);

 theshortestway(R, B, D, P);

 cout<<"从0点到各点的最短距离"<<endl;

 for(int m = 0; m < VEX; m++)

       cout<<D[m]<<" ";

 cout<<endl;

}

调试运行:

2.找零问题

基本思想:将要找零的数跟各种面值的零钱按由大到小比较,用除法得到个面值的找零数,直到全部找零。

#include<iostream>

using namespace std;

int const ZUSHU = 5;

int Ling[] = {50,20,10,5,1};

int GeShu[ZUSHU];

void ZhaoLing(int n)

{     for(int i=0;i<ZUSHU;i++)

       {     GeShu[i] = n / Ling[i];         n = n % Ling[i];     }

}

void main()

{     cout<<"请问你要找零多少钱(单位:元)"<<endl;

int m;      cin>>m;

       ZhaoLing(m);

       cout<<"需要找";

       for (int j = 0; j < ZUSHU-1; j ++)

              cout<<GeShu[j]<<"张"<<Ling[j]<<"元"<<",";

       cout<<GeShu[ZUSHU-1]<<"张"<<Ling[ZUSHU-1]<<"元"<<endl;

}

截图如下:

3.多机调度

分析:

(1)采用最长处理时间作业优先的贪心选择策略可以设计出解多机调度问题的较好的近似算法。

按此策略,当n≤m时,只要将机器i的[0, ti]时间区间分配给作业i即可,算法只需要O(1)时间。

当n>m时,首先将n个作业依其所需的处理时间从大到小排序。然后依此顺序将作业分配给空闲的处理机。算法所需的计算时间为O(nlogn)。

代码:

#include <iostream>

using namespace std;

#define N 10           //作业数

#define M 3                   //机器数

static int time[N]={2,8,18,32,50,72,98,128,182,200},s[M]={0,0,0};

void sort(int t[],int n)

{for(int k=0;k<n-1;k++)   //用选择法将处理时间从大到小排序

{int j=k;

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

        if (t[i]>t[j])   j=i;

        int temp=t[j];t[j]=t[k];t[k]=temp;

}

}

int max(int t[],int num)   //max函数求解处理时间总和最长

{int max=t[0];

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

if(max<t[i]) max=t[i];

 return max;

}

int min(int t[],int m)

{int min=0;    //min记录目前处理作业时间和最小的机器号

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

if(s[min]>s[i]) min=i;

 return min;

}

int set_work1(int t[],int n)

{int m=0;

 for(int i=0;i<n;i++)   //分派作业

s[m++]+=t[i];

 return max(s,N);

}

int set_work2(int t[],int n)

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

s[min(s,M)]+=t[i];

 return max(s,M);

}

void main()

{sort(time,N);

 cout<<"最短作业时间为:";

 if(M>=N)           //作业数小于机器数

 cout<<set_work1(time,N)<<endl;

 else

 cout<<set_work2(time,N)<<endl;

}

截图如下:

4.去掉数的最小新数算法代码:

  #include <iostream>

using namespace std;

int main()

{  

   const int max_len=240;

   char n[max_len];

   cout<<"请输入一个高精度正整数:";

   cin>>n;

   int len = strlen(n);

   int len1;

   cout<<"请输入要去掉的数字个数:";

   cin>>len1;

   // int len1 = strlen(s);

   if (len1>len)

   {

      cout<<"Please input right number!";

   }

   while (len1>0)

   {

      int i=1;

      while ((i<len)&&n[i]<n[i+1])

      {  i++;}

      len1--;

     

   for (;i<len;i++)

   {

      n[i]=n[i+1];

   }

   }

   while ((len>1)&&(n[1]=='0'))

   {

      for (int j=1;j<len-1;j++)

      {

      n[j]=n[j+1];

      }

   }

// for(i=0;i<len-len)

    cout<<n;

   return 0;

}

  截图如下:

 

实验过程分析:

   实验过程主要是对代码进行理解,因为代码老师基本上都给了。主要是对算法的理解。用贪心算法来实现一些程序比较容易。

相关推荐