每日一道贪心算法(背包+小船过河问题)

贪心算法(百度百科)

贪心算法(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,他所做出的是在某种意义上的局部最优解

贪心算法不是对所有问题都能得到整体最优解,关键是贪心策略的选择,选择的贪心策略必须具备无后效性,即某个状态以前的过程不会影响以后的状态,只与当前状态有关。


贪心算法(自我理解)

将整体求解拆分成局部求解,在局部中求到最优解,从最大开始出发,从最小开始出发,或者从两者最出发考虑问题,进而得到局部最优解,再延伸到全局解,但是这个全局解只是相对的,并不一定是最优的解法,但是可以接近最优解,从一个背包问题中就可以看出来:零一背包,部分背包,完全背包。很容易证明,背包问题不能使用贪心算法。但是可以通过动态规划的方式去解决,这个算法我们日后再看~


[背包问题]有一个背包,背包容量是M=150。有7个物品,物品可以分割成任意大小。
要求尽可能让装入背包中的物品总价值最大,但不能超过总容量。
物品 A B C D E F G
重量 35 30 60 50 40 10 25
价值 10 40 30 50 35 40 30
记得当时学算法的时候,就是这个例子,可以说很经典。
分析:
目标函数: ∑pi最大
约束条件是装入的物品总重量不超过背包容量,即∑wi<=M( M=150)
(1)根据贪心的策略,每次挑选价值最大的物品装入背包,得到的结果是否最优?
(2)每次挑选所占重量最小的物品装入是否能得到最优解?
(3)每次选取单位重量价值最大的物品,成为解本题的策略?
贪心算法是很常见的算法之一,这是由于它简单易行,构造贪心策略简单。但是,它需要证明后才能真正运用到题目的算法中。一般来说,贪心算法的证明围绕着整个问题的最优解一定由在贪心策略中存在的子问题的最优解得来的。
对于本例题中的3种贪心策略,都无法成立,即无法被证明,解释如下:
(1)贪心策略:选取价值最大者。反例:
W=30
物品:A B C
重量:28 12 12
价值:30 20 20
根据策略,首先选取物品A,接下来就无法再选取了,可是,选取B、C则更好。
(2)贪心策略:选取重量最小。它的反例与第一种策略的反例差不多。
(3)贪心策略:选取单位重量价值最大的物品。反例:
W=30
物品:A B C
重量:28 20 10
价值:28 20 10
根据策略,三种物品单位重量价值一样,程序无法依据现有策略作出判断,如果选择A,则答案错误。
值得注意的是,贪心算法并不是完全不可以使用,贪心策略一旦经过证明成立后,它就是一种高效的算法。比如,求最小生成树的Prim算法和Kruskal算法都是漂亮的贪心算法。



小船过河问题

题目:

只有一艘船,能乘2人,船的运行速度为2人中较慢一人的速度,过去后还需一个人把船划回来,问把n个人运到对岸,最少需要多久。

思路:

先对题目进行分析:一艘船只能一次乘坐两个人,并且按照两人中较慢的那个人速度划过去,就比如甲单独过河只需要1分钟就过去了,乙单独过河需要2分钟,那么甲和乙一起过河的话就需要2分钟了(按照最慢的乙的速度),本题看上去有很多的过岸的方式,但题目中说到了最少需要的时间,这时候就要从最优去分析了,这是立马想到的就是贪心算法:要么从最快开始分解,要么从最慢开始分解,显然本题从最慢的开始着手去考虑,因为船速是按照两人中较慢那个人的速度,那么最慢和较慢的两个两个人成了我们解题的关键,如何把最慢和第二慢的这两个人送到河的对岸成为了最核心的问题,送完之后,再送第三慢和第四慢的,以此类推,这就是贪心算法的核心思想了,小伙伴们会想到很多的过岸的方式,但从最快时间考虑,

扫描二维码关注公众号,回复: 4826026 查看本文章

小伙伴们首先想到的就是这种方法:

1.让最快的和最慢的乘船过河,然后最快的乘船回来,再让最快的和第二慢的一同乘船过河,之后最快的乘船回来(再去接更多的人~)这是最常想到的方法,也是解法相对最优的情况,花费时间2*time[0]+time[n-2]+time[n-1](下标从0开始,所以数组中n-1代表第n个)

还有一种方法也是最优解的有力竞争~

2.先让最快的和较快的乘船过去,接着最快的乘船回来,再让最慢的和第二慢的乘船过去,较快的乘船回来,这是第二种方式(从情理上来讲相对合理,因为第一种的话最快的太累了,哈哈)这种方法花费的时间time[0]+2*time[1]+time[n-1]

除了以上两种情况,小伙伴们可以考虑一下,其他的方式花费的时间更多(实际上如果这些人之间速度相差很多选第二种较快,如果速度相差很小,就要根据具体的数据进行分析了~)

还可以用到前面学到的递归算法进行解题~有兴趣的小伙伴可以自己尝试一下哦~

下面是源代码:

package 贪心算法;

import java.util.Arrays;
import java.util.Scanner;

public class 小船过河 {
public static void main(String[] args) {
int sum=0;
Scanner sc = new Scanner(System.in);
//t为共有多少组过河的人
//int t=sc.nextInt();
//for(int i=t;i>0;i--){
//n为要过河的人数
int n=sc.nextInt();
int a[]=new int[n];
for(int i=0;i<n;i++){
a[i]=sc.nextInt();
}
sc.close();
//对数组a进行升序排列
Arrays.sort(a);//java自带的排序函数,自己也可以实现。
//实现循环,每次把最慢的和较慢的人送过去,因此每次减去2
while(n>3){
//Math包自带的判断大小的函数,可直接使用,也可以写一个单独的函数实现
sum=Math.min(sum+a[0]+2*a[1]+a[n-1], sum+2*a[0]+a[n-1]+a[n-2]);
n-=2;
}
System.out.println(sum);
//如果是三个人直接把三个人的时间加起来就可以了~
if(n==3){
sum+=a[0]+a[1]+a[2];
}
//如果是两个人,时间直接是较慢的人的。
else if(n==2){
sum+=a[1];
}
//一个人的话,时间就是他本身~
else{
sum+=a[0];
}
System.out.println(sum);
//}
}
}


唯君不争,天下莫能与之争~


结语:

成功的路上并不拥挤,因为懂得坚持的人不多。

我是爱编程的小萝卜头,我为编程代言~

猜你喜欢

转载自blog.csdn.net/qq_38691107/article/details/80779700