贪心算法
来源:互联网 发布:mac os 10.11还是10.9 编辑:程序博客网 时间:2024/06/06 09:37
贪心算法
贪心算法是从某个初始解出发,向给定的目标递推,递推的每一步就是做一个最佳的贪心选择,不断地把一个问题归纳成更小的相似问题。并期望通过局部的选择产生一个全局最优的解。
贪心法的基本思路
从问题的初始解出发,逐步逼向目标,尽可能快地找更好的解,当算法达到某一步不再继续前进时,算法宣告停止。
例1 、部分背包问题
给一个最大载重量为M的背包和N种货物(货物均可分割)。已知第i种货物的重量为Wi,其总价值为Pi,设定M,Wi,Pi均为整数,编程确定一个装货方案,使得装入背包中的货物总价值最大。
分析:
设背包中放入的第i种货物的重量为Xi,则问题的实质是确定一组X1,X2,…,Xn,使得∑Xi*(Vi/Wi)最大。理论上可以采用枚举的方法枚举出所有情况,然后选择出最优,但算法在时间上很难承受。是否有更好的方法?
问题的目标是找∑Xi*(Vi/Wi)最大,如果在装入相同重量的货物时,单位重量价值越大,则背包所装货物的总价值越大。因此容易证明,在装货过程中,为保证背包中所装货物总价值最大,应尽可能选择单位重量价值越大的物品优先装入,下面是算法的简单描述:
Procedure bag;
Begin
读入数据;
计算货物的单位价值Ti,并将它们按从大到小排序;
依次选择单位重量较大的货物放入背包,直至不能放入;
输出最大价值总和S和装货方案;
End;
{程序由同学自行完成程序 }
例2、均分纸牌(NOIP2002tg)
有 N 堆纸牌,编号分别为 1,2,…, N。每堆上有若干张,但纸牌总数必为 N 的倍数。可以在任一堆上取若干张纸牌,然后移动。移牌规则为:在编号为 1 堆上取的纸牌,只能移到编号为 2 的堆上;在编号为 N 的堆上取的纸牌,只能移到编号为 N-1 的堆上;其他堆上取的纸牌,可以移到相邻左边或右边的堆上。现在要求找出一种移动方法,用最少的移动次数使每堆上纸牌数都一样多。例如 N=4,4 堆纸牌数分别为:
① 9 ② 8 ③ 17 ④ 6
移动3次可达到目的:
从 ③ 取 4 张牌放到 ④ (9 8 13 10) -> 从 ③ 取 3 张牌放到 ②(9 11 10 10)-> 从 ② 取 1 张牌放到①(10 10 10 10)。
[输 入]:键盘输入文件名。
文件格式:N(N 堆纸牌,1 <= N <= 100)
A1 A2 … An (N 堆纸牌,每堆纸牌初始数,l<= Ai <=10000)
[输 出]:输出至屏幕。格式为:所有堆均达到相等时的最少移动次数。
[输入样例]
a.in:
4
9 8 17 6
[输出样例]
3
分析:
设a[i]为第i堆纸牌的张数(0<=i<=n),v为均分后每堆纸牌的张数,s为最小移到次数。我们用贪心法,按照从左到右的顺序移动纸牌。如第i堆(0
var i,n,s:integer;v:longint; a:array[1..100]of longint;begin readln(n);v:=0; for i:=1 to n do begin read(a[i]); inc(v,a[i]) end; v:=v div n; {每堆牌的平均数} for i:=1 to n-1 do if a[i]<>v then {贪心选择}begin inc(s); {移牌步数计数} a[i+1]:=a[i+1]+a[i]-v; {使第i堆牌数为v} end;{then} writeln(s);end.
例3 (NOIP1998tg)
设有n个正整数,将他们连接成一排,组成一个最大的多位整数。例如:n=3时,3个整数13,312,343,连成的最大整数为:34331213
又如:n=4时,4个整数7,13,4,246连接成的最大整数为7424613
输入:N
N个数
输出:连接成的多位数
分析:
此题很容易想到使用贪心法,在考试时有很多同学把整数按从大到小的顺序连接起来,测试题目的例子也都符合,但最后测试的结果却不全对。按这种贪心标准,我们很容易找到反例:12,121 应该组成12121而非12112,那么是不是相互包含的时候就从小到大呢?也不一定,如:12,123 就是12312而非12123,这样情况就有很多种了。是不是此题不能用贪心法呢?
其实此题是可以用贪心法来求解,只是刚才的贪心标准不对,正确的贪心标准是:先把整数化成字符串,然后再比较a+b和b+a,如果a+b>b+a,就把a排在b的前面,反之则把a排在b的后面。
源程序:
var s:array[1..20] of string; t:string;i,j,k,n:longint;begin readln(n); for i:=1 to n do begin read(k); str(k,s[i]) end; for i:=1 to n-1 do for j:=i+1 to n do if s[i]+s[j]<s[j]+s[i] then begin{交换} t:=s[i]; s[i]:=s[j]; s[j]:=t end; for i:=1 to n do write(s[i]);end.
一般来说,适用于贪心策略求解的问题具有如下特点:
1、可以通过局部的贪心选择来达到问题的全局最优解。运用贪心策略解题,一般来说需要一步步地进行贪心选择,结束一次贪心选择之后,原问题将变为一个相似的,但规模更小的问题,而后的每一步都是当前看似最佳的选择,且每一个选择都仅做一次。
2、原问题的最优解包含子问题的最优解,即问题具有最优子结构的性质。
需要注意的是,并不是所有具有最优子结构的问题都可以用贪心策略求解,如果将上题改为0/1背包问题,即规定在装入货物时,对某件货物要么不装,要么必须全部装入这一条件,就不能用上述方法,如有三种货物的重量分别为10,20,30,其对应总价值分别是60,100,120,背包最大载重量是50,如果仍按贪心法来处理,则最后背包的总价值为60+100=160,而真正的最优解是100+120=220,所在在处理这类问题时,不能用贪心法,而要用动态规划来处理。
练习:
删数问题
输入一个高精度的正整数n(<=240位),去掉其中任意s个数字后剩下的数字按原左右次序组成一个新的正整数。编程对给定的n和s,寻找一种方案,使得剩下的数字组成的新数最小。
输入:n
S
输出:最后剩下的最小数。
样例输入:178543
4
样例输出:13
源程序如下:
program tanxin;var st:string; n,l,j:byte;begin readln(st); l:=length(st); read(n); j:=1; while n>0 do begin j:=1; while(j<l)and(st[j]<=st[j+1]) do inc(j); delete(st,j,1); dec(n); end; while st[1]='0' do delete(st,1,1); write(st);end.
- 【贪心】贪心算法总结
- 贪心算法
- 贪心算法
- 贪心算法
- 贪心算法
- 贪心算法
- 贪心算法
- 贪心算法
- 贪心算法
- 贪心算法
- 贪心算法
- 贪心算法
- 贪心算法
- 贪心算法
- 贪心算法
- 贪心算法
- 贪心算法
- 贪心算法
- 弹出对话框
- BirdGame游戏
- T-SQL学习之路之数据库完整性之默认值
- Character Controller深度解析
- C++设计模式[三]建造者模式
- 贪心算法
- 理解FCM聚类算法中拉格朗日乘子法(Lagrange Multiplier) 和KKT条件
- Bitmap.createBitmap几个参数的理解、
- IP地址分类与子网划分
- 数组
- 有漏洞的代码
- 欢迎使用CSDN-markdown编辑器
- 自己动手丰衣足食之完数_黑马程序员
- 把毫秒ms换算成xx:xx:xx形式