一.实验目的
理解贪心算法的基本原理,了解适用贪心算法的问题类型,并能设计相应的算法。
二.实验基本步骤
- 选定实验题目,仔细阅读实验要求,设计好输入输出,按照分治法的思想构思算法,选取合适的存储结构实现应用的操作。
- 实验要有详细的测试记录,包括各种可能的测试数据。
三.实验内容
1.最优服务次序问题。
问题描述:设有n个顾客同时等待一项服务,顾客i需要的服务时间为ti,(1<=i<=n)。应如何安排n个顾客的服务次序才能使平均等待时间达到最小?(平均等待时间是n个顾客等待服务时间总和除以n)
输入:第一行为一个正整数n,表示有n个顾客
第二行为n个正整数,表示n个顾客需要的服务时间
输出:最小平均等待时间。
答:
运行代码:
#include<iostream>
#include<algorithm>
using namespace std;
int main(){
int n,a[1000];
cout<<"排队人数:"<<endl;
cin>>n;
int i;
cout<<"各自等待时间:"<<endl;
for(i=0;i<n;i++){
cin>>a[i];
}
sort(a,a+n);
int sum=0;
for(i=0;i<n;i++){
sum+=a[i]*(n-i);
}
cout<<"平均最短时间:"<<sum/(n*1.0)<<endl;
return 0;
}
运行截图:
2.删数问题。
给定一个高精度正整数a, 去掉其中k个数字后按原左右次序将组成一个新的正整数。对给定的a, k寻找一种方案,使得剩下的数字组成的新数最小。
解:应用贪心算法设计求解
(1) 设计要点
操作对象为n位高精度数,存储在数组a中。
在整数的位数固定的前提下,让高位的数字尽量小,整数的值就小。这就是所要选取的贪心策略。
每次删除一个数字,选择一个使剩下的数最小的数字作为删除对象。
当k=1时,对于n位数构成的数删除哪一位,使得剩下的数据最小。删除满足如下条件的a[i]:它是第一个a[i]>a[i+1]的数,如果不存在则删除a[n](最后一位数字,具体实现时请注意下标表示方法)。
当k>1(当然小于n),按上述操作一个一个删除。每删除一个数字后,后面的数字向前移位。删除一个达到最小后,再从头开始,删除第2个,依此分解为k次完成。
若删除不到k个后已无左边大于右边的降序或相等,则停止删除操作,打印剩下串的左边n-k个数字即可(相当于删除了若干个最右边的数字)。
答:代码为:
#include <stdio.h>
#include <string.h>
int main()
{
char n[101]; //将输入的数字存储为字符串形式
int s,len,i; //s:删去的数字数 len:输入数字的长度 i:计数变量
printf("输入数字:\n");
scanf("%s",n);
printf("输入要删除的位数:\n");
scanf("%d",&s);
if(s == strlen(n)){ //如果删除的位数=输入的数字位数,则为0
printf("0");
return 0;
}
while(s>0) //s=0时,删数结束。while循环执行逐个删数
{
i = 0; //从i=0开始遍历,寻找第一个递减数列。
len = strlen(n); //储存n的位数
while(i < len && n[i] <= n[i+1]) //当i<len并且n[i]<=n[i+1]时,不构成递减数列,i++
i++;
while(i<len) //构成递减数列时,删除n[i],之后字符依次前移
{
n[i]=n[i+1];
i++;
} //i=len-1时,字符迁移完毕。
s--; //继续删除数字
}
//删除数字结束后,即s=0时
//有时会出现以下情况,删除过程中有可能会使原来包含零的数字串变成若干个以0开始的序列,如80056,
//第一次删除8后,变成0056,高位的0是无效的,程序中应该处理掉。
i=0;
len = strlen(n);
while(n[i]=='0'&&i<len) //从i=0开始遍历,直到不为0的那一位
i++;
if(i==len) //如果i=len时还没有出现不为零的位,则该数为零。
printf("0\n");
else //找到不为0的那一位,当作首位,输出。
{
for(i=i;i<len;i++)
printf("%c",n[i]);
printf("\n");
}
return 0;
}
运行部分截图:
3.最优分解问题。
问题描述:设n是一个正整数,要求将n分解为若干互不相同的自然数之和,且这些自然数的乘积最大。
输入:正整数n
输出:计算的最大乘积。
如输入10,则输出30.
提示:若a+b的值为一个常量,则a-b的绝对值越小,ab值越大。贪心策略:将n分成从2开始的连续自然数之和,如果最后剩下一个数,则将此数在后项优先的方式下均匀地分给前面各项。
答:运行代码为
// ####################################################
// 贪心算法解决 '整数最优分解' 问题.
// ####################################################
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define STR_LEN 100
#define TRUE 1
#define FALSE 0
int DoIntOptDecomposition( int n, int *a );
//分解整数,并且计算乘积
int DoIntOptDecomposition( int n, int *a )
{
int MaxProduct = 1; // 乘积
int k = 1;
int count = 2; // 从 '2' 开始处理
//将 'n' 分成连续的自然数
while((n - count) > 0)
{
n -= count;
a[k++] = count++;
}
//设置'a'数组的结束标记
a[k--] = -1;
//将最后剩下的一个数,以后项优先的方式均匀分给前面各项
int basicsNum = n / (k - 1); //前面项平均分配到的最低数量
int surplusNum = n % (k - 1); //剩余多的,分配给后项,每项分 '1'
while(k > 0)
{
a[k] += basicsNum;
if( surplusNum > 0 )
{
a[ k ] += 1;
surplusNum --;
}
k --;
}
//计算乘积
//k此时值为 '0',所有自增 '1'
while(k++, a[k] > 0)
{
MaxProduct *= a[k];
}
return MaxProduct;
}
int main( void )
{
char StrN[ STR_LEN ];
int *a, *p;
int n, MaxProduct;
int IsStop;
IsStop = FALSE;
while ( !IsStop )
{
system( "cls" );
// 输入字符串 'A' ...
printf( "\n\n\t请输入 < 待分解的整数 > , 输入 < q / Q > 表示结束 : " );
scanf( "%s", StrN );
if ( strlen( StrN ) > 0 )
IsStop = ( ( StrN[ 0 ] == 'q' ) || ( StrN[ 0 ] == 'Q' ) );
else
printf( "\t输入的整数不能为空 !\n\n" );
if ( !IsStop )
{
a = ( int * )malloc( STR_LEN * sizeof( int ) );
// 得到输入的整数值 ...
n = atoi( StrN );
// 整数最优分解 ...
MaxProduct = DoIntOptDecomposition( n, a );
// 显示计算结果 ...
printf( "\n\t< 整数最优分解乘积为:%d > \n", MaxProduct );
printf( "\n\t< 分解因子值为 > \n" );
int i = 1;
while(a[i] > 0)
{
printf("\t%d", a[i]);
i++;
}
free( a );
printf( "\n\n" );
system( "PAUSE" );
}
}
printf( "\n\n" );
system( "PAUSE" );
return 0;
}
运行截图:
4.多机调度问题
利用贪心法设计算法求解如下问题:
要求给出一种作业调度方案,使所给的n个作业在尽可能短的时间内由m台机器加工处理完成。约定,每个作业均可在任何一台机器上加工处理,但未完工前不允许中断处理。作业不能拆分成更小的子作业。
这个问题是一个NP完全问题,到目前为止还没有一个有效的解法。对于这一类问题,用贪心选择策略有时可以设计出较好的近似算法。
分别采用以下两种贪心策略实现算法求解,对比实验结果,分析哪种贪心策略更适合应用于多机调度问题。
(1)最长处理时间作业优先的贪心选择策略。
(2)最短处理时间作业优先的贪心选择策略。
至少采用两组实验数据进行实验,请参考实验数据一,自己再设计一组实验数据。
实验数据一:7个独立的作业由3台机器加工处理,各作业所需的处理时间为:{2,14,4,6,16,5,3}。
答:源代码为:
#include<iostream>
using namespace std;
int sort(int a[],int n)
{
for(int i=0;i<n-1;i++)
{
for(int j=1;j<n-i-1;j++)
{
if(a[j]>a[j+1])
{
int temp = a[j];
a[j] = a[j+1];
a[j+1] = temp;
}
}
}
return *a;
}
void longTimefirst(int a[],int n,int m)
{
cout<<"/----最长作业优先算法---/"<<endl;
int i=0;int work[3];
int MIN=0,MAX=0;
sort(a,n);
for(i=0;i<m;i++)
work[i]=0;
if(m>n)
cout<<"最长作业优先算法耗费时间:"<<a[n-1]<<endl;
else
{
while(n>0)
{
for(int j=1;j<m;j++)
{
if(work[MIN]>work[j])
MIN=j;
work[MIN]=work[MIN]+a[n-1];
if(n==0)
break;
cout<<"作业"<<n<<"("<<a[n-1]<<"):时间和最小的机器号数位"<<MIN<<",时间和为"<<work[MIN]<<endl;
n--;
}
}
for(int k=0;k<m;k++)
if(MAX<work[k])
MAX=work[k];
cout<<"最长作业优先算法耗费时间:"<<MAX<<endl;
}
}
void shortTimefirst(int a[],int n,int m)
{
cout<<"/----最短作业优先算法---/"<<endl;
int i=0,x=0;int work[3];
int MIN=0,MAX=0;
sort(a,n);
for(i=0;i<m;i++)
work[i]=0;
if(m>n)
cout<<"最短作业优先算法耗费时间:"<<a[n-1]<<endl;
else
{
while(x<n)
{
for(int j=0;j<m;j++)
{
if(work[MIN]>work[j])
MIN=j;
work[MIN]=work[MIN]+a[x];
if(x==n)
break;
cout<<"作业"<<x+1<<"("<<a[x]<<"):时间和最小的机器号数位"<<MIN<<",时间和为"<<work[MIN]<<endl;
x++;
}
}
for(int k=0;k<m;k++)
if(MAX<work[k])
MAX=work[k];
cout<<"最短作业优先算法耗费时间:"<<MAX<<endl;
}
}
void arriveTimefirst(int a[],int n,int m)
{
cout<<"/----作业到达时间优先算法---/"<<endl;
int i=0,x=0;int work[3];
int MIN=0,MAX=0;
for(i=0;i<m;i++)
work[i]=0;
if(m>n)
cout<<"作业到达时间优先算法耗费时间:"<<a[n-1]<<endl;
else
{
while(x<n)
{
for(int j=0;j<m;j++)
{
if(work[MIN]>work[j])
MIN=j;
}
work[MIN]=work[MIN]+a[x];
if(x==n)
break;
cout<<"作业"<<x+1<<"("<<a[x]<<"):时间和最小的机器号数位"<<MIN<<",时间和为"<<work[MIN]<<endl;
x++;
}
for(int k=0;k<m;k++)
if(MAX<work[k])
MAX=work[k];
cout<<"作业到达时间优先算法耗费时间:"<<MAX<<endl;
}
}
int main()
{
int n=7,m=3;
int a[7]={2,14,4,6,16,5,3};
arriveTimefirst(a,7,3);
cout<<endl;
longTimefirst(a,7,3);
cout<<endl;
shortTimefirst(a,7,3);
cout<<endl;
return 0;
}
换一组实验数据: