贪心算法

来源:互联网 发布: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.     
0 0
原创粉丝点击