贪心算法实验题
东 华 大 学
《算法分析设计与综合实践》实验报告
学生姓名:曹晨学号:171310402 指导教师:章昭辉
实验时间:2019-4-9 实验地点:图文信息大楼三号机房
-
实验名称
贪心算法
-
实验目的
- 理解贪心算法的概念。
- 掌握贪心算法的基本要素。
- 了解适用贪心算法的问题类型,并能设计相应的算法。
-
实验内容
-
最优服务次序问题
问题描述:设有n个顾客同时等待一项服务,顾客i需要的服务时间为ti(1<=i<=n)。应如何安排n个顾客的服务次序才能使平均等待时间最小?平均等待时间是n个顾客等待服务时间的总和除以n。
算法设计:对于给定的n个顾客需要的服务时间,计算最优服务次序。
数据输入:由文件input.txt提供输入数据。第1行是正整数n,表示有n个顾客。接下来的1行中,有n个正整数,表示n个顾客需要服务的时间。
结果输出:将计算的最小平均等待时间输出到文件output.txt。
-
非单位时间任务安排问题
问题描述:具有截止时间和误时惩罚的任务安排问题可描述如下
- 给定n个任务的集合S={1,2,….,n};
- 完成任务i需要ti时间,1<=i<=n;
- 任务i的截止时间di(1<=i<=n),即要求任务i在时间di之前结束
-
任务i的误时时间wi(1<=i<=n),即任务i未在时间di之前结束,将招致wi的惩罚,若按时完成,则无惩罚。
任务安排要求确定一个S的时间表(最优时间表)使得总误时惩罚达到最小
算法设计:对于给定的n个任务,计算总误时惩罚最小的最优时间表
数据输入:由文件input.txt给出输入数据。第1行是一个正整数n,表示任务数。接下来的n行中,每行有3个正整数a、b、c,表示完成相应任务需要时间a,截止时间为b,误时惩罚值为c。
结果输出:将计算的总误时时间输出到文件output.txt。·
-
-
实验过程
-
研究一个实例:有10个顾客,所需服务时间分别为56 12 1 99 1000 234 33 55 99 812
用t[i]来表示每个顾客所需的服务时间,T[i]表示等待的时间。假设已知一个最优服务次序A={t(1),t(2),…t(n)};按照此顺序每个顾客的等待时间为:T(1)=t(1),T(2)=t(1)+t(2),…….,T(n)=t(1)+t(2)+….+t(n);总的等待时间为sum=T(1)+T(2)+….+T(n);可以看出t(1)被加了n次, t(2)被加了n-1次,依此类推,直到t(n)被加一次后结束,除以n就等于平均等待服务时间。运用贪心策略,即最短服务时间优先。将t[i]按照递增的顺序排序,所得的序列就是最优服务序列。
-
顾客编号 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
服务时间 |
1 |
12 |
33 |
55 |
56 |
99 |
99 |
234 |
812 |
1000 |
等待时间 |
1 |
13 |
46 |
101 |
157 |
256 |
355 |
589 |
1401 |
2401 |
算法描述:
1 //求最短平均等待时间的函数 2 int Findbest(int *t,int n)//n是顾客的个数,t[n]用来存放每个顾客所需的服务时间 3 { 4 sort(t,t+n);//从小到大递增排列数组t 5 int sum=0;//总的等待时间初始化0 6 for(int i to n) 7 sum+=t[i]*(n-i);//从1到n遍历最优服务次序,并加上各个等待服务时间 8 sum=sum/n;//求平均等待服务时间 9 return sum; 10 }
-
定义一个结构体Task包含三项数据。
struct Task
{
int a;//所需时间
int b;//截止时间
int c;//惩罚值
};
首先将任务按其截止时间递增排序。假设对于任务1,2,…..,n,如果截至时间为d,则最小的误时惩罚为p(i,d);
其中p(i,d)={p(i-1,d)+t[i].c,p(i-1,min(d,t[i].a))}
p(i-1,d)+t[i].c表示不做第i个任务时的误时惩罚值,p(i-1,min(d,t[i].a))表示要做第i个任务时的误时惩罚值,条件是必须在截止时间之前做完它。
对于第一个任务,如果时间小于它完成时间,那么遭受惩罚,否则的话做第一个任务
对于第i个任务,如果时间小于它的完成时间,不能执行,惩罚值等于上一个任务当前时间加上第i个任务的惩罚值,如果大于则比较不能完成时的惩罚值和完成这个任务所得的惩罚值,取二者最小的那个。下图是一个实例的表。该例子为n=7;(1,4,70)(2,2,60)(1,4,50)(1,3,40)(1,1,30)(1,4,20)(3,6,80);
;
任务编号 |
截至时间0 |
1 |
2 |
3 |
4 |
5 |
6 |
0 |
30 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
90 |
60 |
30 |
30 |
30 |
30 |
30 |
2 |
130 |
90 |
60 |
30 |
30 |
30 |
30 |
3 |
200 |
130 |
90 |
60 |
30 |
30 |
30 |
4 |
250 |
180 |
130 |
90 |
60 |
60 |
60 |
5 |
270 |
200 |
150 |
110 |
80 |
80 |
80 |
6 |
350 |
280 |
230 |
190 |
160 |
150 |
110 |
算法描述:
1 //sort的自定义排序函数 2 bool comp(Task x,Task y) 3 { 4 if(x.b<y.b)//根据截至时间的大小进行非减排序 5 return true; 6 else return false; 7 } 8 //求最小惩罚值的函数 9 int Leastime(Task *t,int n)//t数组代表任务的三个参数,n代表任务的个数 10 { 11 sort(t,t+n,comp);//根据缩写的comp函数排列数组(非减) 12 int d=t[n-1].b;//最大的截止时间d 13 int p[n][d+1];//二维数组p来代表p(i,d)时的最小惩罚数 14 memset(p,999,sizeof(p));//初始化数组 15 for(int i=0 to d)//遍历二维数组的第一行,有时间做该任务,惩罚值为0,否则惩罚值为t[0].c 16 p[0][i]=(i>=t[0].a)?0:t[0].c; 17 for(int i=1 to n)//遍历剩下的二维数组, 18 for(int j=0 to d) 19 p[i][j]=p[i-1][j]+t[i].c;//首先考虑不做当前任务的惩罚值 20 int h=t[i].b<j?t[0].b:j;//如果有时间做的话 21 if(h>=t[i].a&&p[i][j]>p[i-1][[h-t[i].a])//跟不做任务相比较 22 p[i][j]=p[i-1][h-t[i].a]//如果不做任务大于做任务的惩罚值,则做任务,改变p[i]j]的值 23 return p[n-1][d];//返回最小惩罚值 24 }
-
结果分析
-
答案是532
这道题非常的简单,用到了贪心算法的思路,通过短短几步就能求解出答案。
时间复杂度:一个for循环用来求总的等待服务时间
F(n)=n=O(n)
空间复杂度:一个一维数组t[n]用来存放各个顾客所需的服务时间
F(n)=n =O(n);
-
答案是110
这个题目是我觉得用动态规划的方法做出来的,因为有比较,做该任务和不做该任务的比较,贪心算法应该把两种情况都写入表中,但是只选了最优的方法
时间复杂度:
F(n)=n*logn+n*d=O(n*logn+n*d)
空间复杂度:用一维结构体数组分别来存放该任务的三个属性,一个二维数组用来存放在截止时间为d,i个任务的最小惩罚值
F(n)=3*n+n*d=O(n+n*d);
-
- 实验总结
附录:(要求代码里各行要有注释)
见打包文件