首页 > 编程语言 > 详细

贪心算法(二)

时间:2016-02-04 18:57:23      阅读:220      评论:0      收藏:0      [点我收藏+]

例6:排队打水问题

有N 个人排队到R 个水龙头去打水,他们装满水桶的时间为T1,T2,…,Tn 为整数且各不相等,应如何安排他们的打水顺序才能使他们花费的时间最少?

分析:由于排队时,越靠前面的计算的次数越多,显然越小的排在越前面得出的结果越小(可以用数学方法简单证明,这里就不再赘述),所以这道题可以用贪心法解答,基本步骤:

(1)将输入的时间按从小到大排序;

(2)将排序后的时间按顺序依次放入每个水龙头的队列中;

(3)统计,输出答案。

【样例输入】

4 2 {4人打水,2个水龙头}

2 6 4 5 {每个打水时间}

【样例输出】

23 {总共花费时间}

参考程序主要框架如下:

 1 对数组a排序;
 2 Fillchar(S,Sizeof(S),0);
 3 J:=0; Min:=0;
 4 For I:=1 To N Do {用贪心法求解}
 5    Begin
 6       Inc(J);
 7       If J=R+1 Then J:=1;
 8       S[J]:=S[J]+A[I];
 9       Min:=Min+S[J];
10    End;
11 Writeln(Min); {输出解答}

例7:均分纸牌(NOIP2002)

有 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

【样例输出】(a.out)

3

【算法分析】

如果你想到把每堆牌的张数减去平均张数,题目就变成移动正数,加到负数中,使大家都变成0,那就意味着成功了一半!拿例题来说,平均张数为10,原张数9,8,17,6,变为-1,-2,7,-4,其中没有为0 的数,我们从左边出发:要使第1 堆的牌数-1 变为0,只须将-1 张牌移到它的右边(第2 堆)-2中;结果是-1 变为0,-2 变为-3,各堆牌张数变为0,-3,7,-4;同理:要使第2 堆变为0,只需将-3移到它的右边(第3堆)中去,各堆牌张数变为0,0,4,-4;要使第3 堆变为0,只需将第3 堆中的4 移到它的右边(第4 堆)中去,结果为0,0,0,0,完成任务。每移动1 次牌,步数加1。也许你要问,负数张牌怎么移,不违反题意吗?其实从第i 堆移动-m 张牌到第i+1 堆,等价于从第i+1 堆移动m 张牌到第i 堆,步数是一样的。

如果张数中本来就有为0 的,怎么办呢?如0,-1,-5,6,还是从左算起(从右算起也完全一样),第1 堆是0,无需移牌,余下与上相同;再比如-1,-2,3,10,-4,-6,从左算起,第1 次移动的结果为0,-3,3,10,-4,-6;第2 次移动的结果为0,0,0,10,-4,-6,现在第3 堆已经变为0 了,可节省1 步,余下继续。

参考程序主要框架如下:

 1 ave:=0;step:=0;
 2 for i:=1 to n do
 3    begin
 4        read(a[i]); inc(ave,a[i]);   {读入各堆牌张数,求总张数ave}
 5     end;
 6   ave:=ave div n;   {求牌的平均张数ave}
 7   for i:=1 to n do a[i]:=a[i]-ave;   {每堆牌的张数减去平均数}
 8   i:=1;j:=n;
 9   while (a[i]=0) and (i<n) do inc(i);   {过滤左边的0}
10   while (a[j]=0) and (j>1) do dec(j);  {过滤右边的0}
11   while (i<j) do
12     begin
13        inc(a[i+1],a[i]);   {将第i 堆牌移到第i+1 堆中去}
14        a[i]:=0;   {第i 堆牌移走后变为0}
15        inc(step);   {移牌步数计数}
16        inc(i); {对下一堆牌进行循环操作}
17        while (a[i]=0) and (i<j) do inc(i);{过滤移牌过程中产生的0}
18     end;
19 writeln(step);

点评:基本题(较易) 本题有3 点比较关键:一是善于将每堆牌数减去平均数,简化了问题;二是要过滤掉0(不是所有的0,如-2,3,0,-1 中的0 是不能过滤的);三是负数张牌也可以移动,这是辩证法(关键中的关键)。

 

贪心算法(二)

原文:http://www.cnblogs.com/vacation/p/5182060.html

踩
(0)
赞
(0)
   
举报
评论 一句话评论(0)
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!