4+四+贪心算法+习题参考答案
贪心算法求解问题(优化版)

贪心算法求解问题问题分析:此问题为程序最优存储问题,问题要求最后存储的两个磁带上的长度差异就最小。
若在最优解中去掉i个程序段,显然在(n-i)个程序段的存储中应仍是最优解,因为此问题存在最优子结构。
另外,由于每个程序的长度不同,每将一个程序存储到A或者B(用A和B来表示两个磁带上存储程序的集合)后,显然还与后续怎么存储程序有关,即当前结果依赖子问题的结果。
这正是动态规划算法的基本特征,而贪心算法仅在当前状态下做出最好选择,然后再去做子问题的局部最优解最终就是问题的最优解,贪心算法不依赖于将来所做的子问题的解,显示,此问题是一个动态规划问题,是一个0-1背包问题。
虽然对有些问题,贪心算法并不能得到一个最优解,但往往能快速地得到一个近似最优解。
下面就来讨论如何用贪心算法得到近似最优解。
贪心算法思路为了使最后两个磁带的长度差异越小,就先将长度较大的程序优先放入到磁带(此处用A和B分别表示两个磁带)上。
因此排序选择递减排序。
用p[ ]数组来存放第i个程序长度为p[i-1],首先将其按程序长度大小递减的顺序排序,将排好序的各程序下标记录到与与p[]等长的数组a[ ]中。
然后再根据a[]中记录的下标找到相应的程序p[a[i]]放到A或者B 中。
现在就接下就是如何存放来达到近似最优解的问题了开始A和B中没有任何元素(本程序中采用vector动态定义A,B),如果用sumA和sumB来标记A和B中已存入程序的总长度,在存入当前最优解时,先比较sumA和sumB 的大小,将最优解存入到程序总长度较短的那个程序集合,如果长度一样,则存入到A或者B中(本程序是存入到A集合中),可以看到这样存入的话,就会尽量减小A和B长度的差异,从而尽量接近最优存储得到近似最优解之前提交的贪心算法的思想是直接交叉存入到A和B中,那样得到的解在一定程度能得利近似最优解,那种思想只对程序段长度相差小的情况有比较好的结果,因为那种思想大概是将程序段平均到A和B中,在很多情况下不能得到近似最优解。
第4章贪心算法习题(免费阅读)

算法实现题4-5 程序存储问题
数据输入:
第一行是2 个正整数,分别表示文件个数n和磁带的长 度L。接下来的1 行中,有n个正整数,表示程序存放在磁 带上的长度。
结果输出: 最多可以存储的程序数。
输入示例
6 50
2 3 13 8 80 20 输出示例
5
i 012345
x 2 3 13 8 80 20 7
3
算法实现题4-5 程序存储问题
问题描述: 设有n 个程序{1,2,…, n }要存放在长度为L的磁带上。
程序i存放在磁带上的长度是 li,1 ≤ i ≤ n。 程序存储问题要求确定这n 个程序在磁带上的一个
存储方案,使得能够在磁带上存储尽可能多的程序。 编程任务:
对于给定的n个程序存放在磁带上的长度,编程计 算磁带上最多可以存储的程序数。
532.00
10
算法实现题4-6 最优服务次序问题
double greedy( vector<int> x) {
int i,n=x.size(); sort(x.begin(),x.end()); for(i=1;i<n;++i)
x[i] += x[i-1]; double t=0; for(i=0;i<n;++i) t+=x[i]; t /= n;
算法实现题4-5 程序存储问题
int greedy( vector<int> x, int m){
int i=0, sum=0, n=x.size();
sort(x.begin(),x.end());
while(i
if(sum <= m) i++;
第4章-贪心算法-习题

算法实现题4-15 套汇问题
while(1){
cin >> n;
if(n == 0) break;
//输入结束
for(i=0; i < n; ++i) cin >> name[i];
数据输入
第1行有2个正整数n和k,表示汽车加满油后可行驶nkm, 且旅途有k个加油站。接下来的一行中,有k+1个整数,表示 第k个加油站与第k-1个加油站之间的距离。第0个加油站表 示出发地,汽车已加满油。第k+1个加油站表示目的地。
结果输出
计算出的最少加油次数。如果无法到达目的地,则输 出”No Solution”。
3
算法实现题4-5 程序存储问题
问题描述: 设有n 个程序{1,2,…, n }要存放在长度为L的磁带上。
程序i存放在磁带上的长度是 li,1 ≤ i ≤ n。 程序存储问题要求确定这n 个程序在磁带上的一个
存储方案,使得能够在磁带上存储尽可能多的程序。 编程任务:
对于给定的n个程序存放在磁带上的长度,编程计 算磁带上最多可以存储的程序数。
定义:
vector<int> x; 读取数据:
int n; scanf(“%d”, &n); int temp; for (int i=0; i<n; i++){
scanf(“%d”, &temp); x.push_back(temp); }
return t;
}
i 01234567 8 9
x 1 12 33 55 56 99 99 234 812 1000
输入示例 77
输出示例 4
1 2 3 4 5 1 6 6 加油站数
4-4贪心-Dijkstra算法

邹娟-中国大学MOOC单源最短路径给定一个图G = (V , E),其中每条边的权是一个非负实数。
另外给定V 中的一个顶点v ,称为源。
求从源v 到所有其它各个顶点的最短路径。
单源最短路径问题的贪心选择策略:选择从源v 出发目前用最短的路径所到达的顶点,这就是目前的局部最优解。
单源最短路径的贪心算法基本思想:首先设置一个集合S;用数组dis[]来记录v到S中各点的目前最短路径长度。
然后不断地用贪心选择来扩充这个集合,并同时记录或修订数组dis[];直至S包含所有V中顶点。
单源最短路径的贪心算法一个顶点u 属于S 当且仅当从v 到u 的最短路径长度已知。
贪心选择:初始化:S 中仅含有源v 。
Dijkstra 算法Dijkstra 算法的做法是:(1)由近到远逐步计算,每次最近的顶点的距离就是它的最短路径长度。
(2)然后再从这个最近者出发。
即依据最近者修订到各顶点的距离,然后再选出新的最近者。
(3)如此走下去,直到所有顶点都走到。
Dijkstra 算法Procedure Dijkstra {S:={1}; //初始化Sfor i:= 2 to n do //初始化Ddis[i] =C[1, i] ; //初始时为源到顶点i一步的距离for i:=1 to n-1 do {从V-S中选取一个顶点u使得dis[u]最小;将u加入到S中;//将新的最近者加入Sfor w∈V-S do //依据最近者u修订dis[v]dis[w] := min(dis[w] , dis[u]+C[u ,w)}}Dijkstra 算法举例迭代S u dis[2] dis[3] dis[4] dis[5]初始{1} --10∞ 30 1001 {1,2} 2 10 60 301002 {1,2,4} 4 10 5030 903 {1,2,4,3} 3 10 50 30 604 {1,2,4,3,5} 5 10 50 30 603102050100301060由数组D[i]可知:从顶点1到顶点2、3、4、5的最短通路的长度分别为10、50、30和60。
(完整版)4+四+贪心算法+习题参考答案

第四章作业部分参考答案1 • 设有n个顾客同时等待一项服务。
顾客i需要的服务时间为t i,1 i n。
应该如何安排n个顾客的服务次序才能使总的等待时间达到最小?总的等待时间是各顾客等待服务的时间的总和。
试给出你的做法的理由(证明) 。
策略:对t i 1 i n进行排序,h t i2t i n,然后按照递增顺序依次服务i i」2,…,i n即可。
解析:设得到服务的顾客的顺序为j1, j2,..., j n ,则总等待时间为T (n 1)t j i (n 2)t j2 2.? 则在总等待时间T中切的权重最大,切的权重最小。
故让所需时间少的顾客先得到服务可以减少总等待时间。
证明:设t i1 t i2 t i n, ,下证明当按照不减顺序依次服务时,为最优策略。
记按照i1i2 i n 次序服务时,等待时间为T ,下证明任意互换两者的次序,T 都不减。
即假设互换i, j (i j) 两位顾客的次序,互换后等待总时间为T~,则有T T.由于T (n 1)t i1 (n 2)t i2 2t i n2 t,i n 1T (n 1)t i1 (n 2)t i2 (n i)t i i (n j)t i j 2t ti n 2 i n 1T (n 1)t i1 (n 2)t i2 (n i)t i j (n j)t i i 2t i t ii n 2 i n 1则有T~ T (j i)(t i j t i i ) 0.同理可证其它次序,都可以由i1i2 i n 经过有限次两两调换顺序后得到,而每次交换,总时间不减,从而i1i 2 i n 为最优策略2. 字符a ~h 出现的频率分布恰好是前 8个Fibo nacci 数,它们的Huffman编码是什么?将结果推广到n 个字符的频率分布恰好是前n 个Fibonacci 数的情 形。
Fib on acci 数的定义为:F o1,F i 1, F nF n 2 F n 1 if n 1所以a 的编码为:1111111 b 的编码为:1111110 c 的编码为:111110 d 的编码为:11110 e 的编码为:1110 f 的编码为:110 g 的编码为:10 h 的编码为:0 推广到 n 个字符:第 1 个字符: n-1 个 1 ,11 1n1 第 2 个字符: n-2 个1 , 1 个 0, 11 10n2第 3 个字符: n-3 个 1 ,1 个 0, 11 10n3第n-1个字符:1个1 , 1 个 0, 10 第 n 个字符: 1 个 03. 设 p 1,p 2, ,p n 是准备存放到长为 L 的磁带上的 n 个程序,程序 p i 需要的 n 带长为 a i 。
4-贪心算法

二、TSP问题的贪心算法
min ci
i 1 n
从驻地城市开始,每次选择还没到的最近(费用最少)的城市去.
1 2 7
1 4 4
2 4 1
7 4 1
5 3 2 3
5
3
2
3
1
2
5
3
4
Cost=1+3+2+1+7=14
1
2
5
4
3
Cost=1+3+3+1+2=10
template <class Type> Type GreedyTSP(Type **C,int n,int k,int x[ ]){ //从城市k出发用贪心算法求解TSP问题. //最佳解放在x[ ]里,路程(费用)用函数值返回 x[1]=k; int u=k; Type cost=0; for(int i=2; i<=n;i++){ Type min=; int v=0; for(int j=1; j<=n;j++) if(C[u][j]<min){min= C[u][j]; v=j;} cost+= min;x[i]=v; C[v][u]= ;u=v; } cost+=C[u][k]; return cost; }
§4.5 最小生成树
一、最小生成树
设G=(V,E)是一个无向连通带权图(无向连通网),E中每条 边(v,w)的权为c[v][w]. 如果G的一个子图G’是一棵包含G的所有顶点的树,则称G’为 G的生成树. 生成树上各条边权的总和称为该生成树的耗费,在G的所有 生成树中,耗费最小的生成树称为G的最小生成树.
第4章 贪心算法(1)活动安排问题

4.1 活动安排问题
活动安排问题是可以用贪心算法有效求解的很 好例子。
该问题要求高效地安排一系列争用某一公共资 源的活动,使得尽可能多的活动能兼容地使用 公共资源。
4
问题描述
设有n个活动的集合E={1, 2, …, n},其中每个 活动都要求使用同一资源,而在同一时间内只 有一个活动能使用这一资源。
2
贪心算法
例2:若上述硬币面值改为:
一角一分、五分和一分 现在要找给顾客一角五分钱,如何给出硬币? 答案:错:1个一角一分,4个一分
对:3个五分
虽然贪心算法不能对所有问题都得到整体最优 解,但对许多问题它能产生整体最优解。
在一些情况下,即使贪心算法不能得到整体最 优解,其最终结果却是最优解的很好的近似解。
ቤተ መጻሕፍቲ ባይዱ17
0-1背包问题
给定n种物品和一个背包。物品i的重量是wi, 其价值为vi,背包的容量为c。应如何选择装 入背包的物品,使得装入背包中物品的总价 值最大?
说明:在选择装入背包的物品时,对每种物 品i只有2种选择,即装入背包或不装入背包。 不能将物品i装入背包多次,也不能只装入部 分的物品i。
16
3、贪心算法与动态规划算法的 差异
贪心算法和动态规划算法都要求问题具有最 优子结构性质,这是两类算法的一个共同点。
对于具有最优子结构的问题应该选用贪心算 法还是动态规划算法求解?
是否能用动态规划算法求解的问题也能用贪 心算法求解?
下面研究2个经典的组合优化问题,并以此 说明贪心算法与动态规划算法的主要差别。
每个活动i都有一个要求使用该资源的起始时 间si和一个结束时间fi,且si <fi 。
5
问题描述
如果选择了活动i,则它在半开时间区间[si, fi) 内占用资源。若区间[si, fi)与区间[sj, fj)不相交, 则称活动i与活动j是相容的。也就是说,当si≥fj
4-贪心法

应用实例
活动安排问题—算法设计与分析
template<class Type> void GreedySelector(int n, Type s[], Type f[], bool A[]) { A[1] = true; int j = 1; for (int i=2;i<=n;i++) { if (s[i]>=f[j]) { A[i]=true; j=i; } else A[i]=false; } }
贪心法的正确性问题
针对具体问题不同,贪心策略的选择可能有多种 ,如何选择合适的贪心策略并证明该策略的正确 性是贪心算法设计中的一个关键问题。 一般可以通过对算法步数的归纳或通过对问题规 模的归纳来证明贪心法的正确性。
应用实例
活动安排问题
有n个活动申请使用同一个礼堂,每项活动有一个开始时间和一 个截止时间,如果任何两个活动不能同时举行,问如何选择这 些活动,从而使得被安排的活动数量达到最多? 设S={1, 2, …, n}为活动的集合,si和fi分别为活动i的开始和截止 时间,i=1, 2, …, n。定义 活动i与j相容:si ≥ fj或sj ≥fi, i≠j 求S最大的两两相容的活动子集。 蛮力法 动态规划方法
若硬币的面值改为一角一分、五分和一分,要找给顾客的 是一角五分,情况如何?
贪心算法的基本思想
顾名思义,贪心算法总是作出在当前看来最好的 选择。也就是说贪心算法并不从整体最优考虑, 它所作出的选择只是在某种意义上的局部最优选 择。 贪心算法不能对所有问题都得到整体最优解,但 对许多问题它能产生整体最优解。 在一些情况下,即使贪心算法不能得到整体最优 解,其最终结果却是最优解的很好近似。
4—贪心法 Greedy Approach
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
第四章作业 部分参考答案1. 设有n 个顾客同时等待一项服务。
顾客i 需要的服务时间为n i t i ≤≤1,。
应该如何安排n 个顾客的服务次序才能使总的等待时间达到最小?总的等待时间是各顾客等待服务的时间的总和。
试给出你的做法的理由(证明)。
策略:对 1i t i n ≤≤进行排序,,21n i i i t t t ≤≤≤ 然后按照递增顺序依次服务12,,...,ni i i 即可。
解析:设得到服务的顾客的顺序为12,,...,n j j j ,则总等待时间为,2)2()1(1221--+++-+-=n n j j j j t t t n t n T 则在总等待时间T 中1j t 的权重最大,jnt 的权重最小。
故让所需时间少的顾客先得到服务可以减少总等待时间。
证明:设,21n i i i t t t ≤≤≤ ,下证明当按照不减顺序依次服务时,为最优策略。
记按照n i i i 21次序服务时,等待时间为T ,下证明任意互换两者的次序,T都不减。
即假设互换j i ,)(j i <两位顾客的次序,互换后等待总时间为T ~,则有.~T T ≥由于,2)2()1(1221--+++-+-=n n i i i i t t t n t n T,2)()()2()1(1221--+++-++-++-+-=n n j i i i i i i i t t t j n t i n t n t n T ,2)()()2()1(~1221--+++-++-++-+-=n n i j i i i i i i t t t j n t i n t n t n T则有.0))((~≥--=-i j i i t t i j T T同理可证其它次序,都可以由n i i i 21经过有限次两两调换顺序后得到,而每次交换,总时间不减,从而n i i i 21为最优策略。
2. 字符h a ~出现的频率分布恰好是前8个Fibonacci 数,它们的Huffman 编码是什么?将结果推广到n 个字符的频率分布恰好是前n 个Fibonacci 数的情形。
Fibonacci 数的定义为:1,1,11210>+===--n if F F F F F n n n解:前8个数为a , b , c , d , e , f , g , h1, 1, 2, 3, 5, 8, 13, 21Huffman 哈夫曼编码树为:所以a 的编码为:1111111 b 的编码为:1111110 c 的编码为:111110 d 的编码为:11110e 的编码为:1110f 的编码为:110g 的编码为:10 h 的编码为:0推广到n 个字符:第1个字符: n-1个1,1111-n 第2个字符: n-2个1,1个0, 01112-n 第3个字符: n-3个1,1个0, 01113-n ……第n-1个字符:1个1 ,1个0, 10 第 n 个字符:1个0 , 03. 设n p p p ,,,21 是准备存放到长为L 的磁带上的n 个程序,程序i p 需要的带长为i a 。
设L a ni i >∑=1,要求选取一个能放在带上的程序的最大子集合(即其中含有最多个数的程序)Q 。
构造Q 的一种贪心策略是按i a 的非降次序将程序计入集合。
1) 证明这一策略总能找到最大子集Q ,使得∑∈≤Qp ii L a 。
2) 设Q 是使用上述贪心算法得到的子集合,磁带的利用率可以小到何种程度? 3) 试说明1)中提到的设计策略不一定得到使∑∈Qp ii L a /取最大值的子集合。
1)证明:不妨设12...n a a a ≤≤≤,若该贪心策略构造的子集合Q 为},,,{21s a a a ,则s 满足∑∑=+=>+≤si s s si i L a a L a 111、。
要证明能找到最大子集,只需说明s 为可包含的最多程序段数即可。
即证不存在多于s 个的程序集合)(},,,,{~21s k a a a Q k i i i >= ,使得∑∈≤Qp i i L a ~。
反证法,假设存在多于s 个的程序集)(},,,,{~21s k a a a Q k i i i >= ,满足∑=≤kj i L a j 1。
因为12...n a a a ≤≤≤非降序排列,则L a a a a a a a k i i i k s ≤+++≤++++ 2121。
因为s k >且为整数,则其前s+1项满足L a a a a s s ≤++++121 。
这与贪心策略构造的子集和Q 中s 满足的∑=+>+si s sL a a11矛盾。
故假设不成立,得证。
2)磁带的利用率为∑∈Qp ii L a/;(甚至最小可为0,此时任意L a i >或者∑∈<<Qp i i L a )3)按照1)的策略可以使磁带上的程序数量最多,但程序的总长度不一定是最大的,假设},,,{21i a a a 为Q 的最大子集,但是若用1+i a 代替i a ,仍满足∑-=+<+111i k i kL a a,则},,,,{1121+-i i a a a a 为总长度更优子集。
4.答案见后面所附程序。
5. 已知n 种货币12,,,nc c c 和有关兑换率的n n ⨯表R ,其中[,]R i j 是一个单位的货币i c可以买到的货币j c 的单位数。
1)试设计一个算法,用以确定是否存在一货币序列12,,,ki i i c c c 使得:12231[,][,][,]1kR i i R i i R i i > 2)设计一个算法打印出满足1)中条件的所有序列,并分析算法的计算时间。
解:基本解题思想:通过FLOYD 算法求出最大环。
判断最大环的权值之积是否大于1,如果大于1说明可以实现套汇,如果不大于1 说明不能实现套汇。
在求解最大环的同时记录下兑换过程中的步骤。
算法实现的数据结构:int Path[MAX_VERTECX_NUM][MAX_VERTECX_NUM];//用来记录套汇过程中要经过的路径 float value[MAX_VERTECX_NUM][MAX_VERTECX_NUM];//用来记录经过讨回操作后得到的值 //借助图来实现该算法 typedef struct{int vexs[MAX_VERTECX_NUM]; //顶点向量 每种货币对应一个顶点float arc[MAX_VERTECX_NUM][MAX_VERTECX_NUM];//邻接矩阵 存放兑换率信息 int vexnum,arcnum; //图中当前顶点数和弧数 }MGraph; 算法中的关键步骤:for(k=1;k<=G->vexnum;k++) {for(i=1;i<=G->vexnum;i++) {for(j=1;j<=G->vexnum;j++) {if(value[i][k]*value[k][j]>value[i][j])//这里判断是否使兑换率增大,如果增大则记录下来 {value[i][j]=value[i][k]*value[k][j]; Path[i][j]=Path[k][j]; } } }}在输出兑换序列时采用了递归算法:这个算法逆序输出了兑换序列。
void Procedure_print(int i,int j){if(Path[i][j]==i){printf("%d",i);return;}else if(Path[i][j]==0)//输出结点i与结点j之间不存在通路printf("NO path");else{printf("%d ",Path[i][j]);Procedure_print(i,Path[i][j]);//{递归,货币I至J中间顶点}}}此算法的时间复杂度是:O(v^3)算法实现代码:#include<stdio.h>#define MAX_VERTECX_NUM 20#define INT_MIN 0int n;int Path[MAX_VERTECX_NUM][MAX_VERTECX_NUM];float value[MAX_VERTECX_NUM][MAX_VERTECX_NUM];typedef struct{int vexs[MAX_VERTECX_NUM]; //顶点向量可以存储每个顶点的信息float arc[MAX_VERTECX_NUM][MAX_VERTECX_NUM];//邻接矩阵主要存放关于边的信息int vexnum,arcnum; //图中当前顶点数和弧数}MGraph;void CreateDG(MGraph *G){int i,j,k;float w;scanf("%d%d",&(G->vexnum),&(G->arcnum));printf("G->vexnum=%d,G->arcnum=%d\n",G->vexnum,G->arcnum);for(i=1;i<=G->vexnum;i++){G->vexs[i]=i;}for(i=1;i<=G->vexnum;i++){for(j=1;j<=G->vexnum;j++){G->arc[i][j]=INT_MIN;}}for(k=1;k<=G->arcnum;k++){scanf("%d%d%f",&i,&j,&w);G->arc[i][j]=w;}}void ShortestPath_FLOYD(MGraph *G){int i,j,k;for(i=1;i<=G->vexnum;i++){for(j=1;j<=G->vexnum;j++){if(i==j)value[i][j]=1;elsevalue[i][j]=G->arc[i][j];if(G->arc[i][j]>INT_MIN)Path[i][j]=i;elsePath[i][j]=0;}}for(k=1;k<=G->vexnum;k++){for(i=1;i<=G->vexnum;i++){for(j=1;j<=G->vexnum;j++){if(value[i][k]*value[k][j]>value[i][j]){value[i][j]=value[i][k]*value[k][j];Path[i][j]=Path[k][j];}}}}}void Procedure_print(int i,int j){if(Path[i][j]==i){printf("%d",i);return;}else if(Path[i][j]==0)//输出结点i与结点j之间不存在通路printf("NO path");else{printf("%d ",Path[i][j]);Procedure_print(i,Path[i][j]);}}int main(){int i,j;MGraph G;freopen("data.in","r",stdin);freopen("data.out","w",stdout);CreateDG(&G);ShortestPath_FLOYD(&G);i=1;if(value[i][i]>1){printf("%f ",value[i][i]);if(Path[i][i]!=0)printf("%d%d ",i,i);printf("兑换顺序的逆序输出:%d ",i);Procedure_print(i,i);printf("\n");}}4.同学们的几种不同答案构造哈夫曼树思想,将所有的节点放到一个队列中,用一个节点替换两个频率最低的节点,新节点的频率就是这两个节点的频率之和。