贪心算法的基本概念
贪心算法与枚举法的不同之处在于每个子问题都选择最优的情况,然后向下继续进行,且不能回溯,枚举法是将所有情况都考虑然后选出最优的情况。
贪心算法,在对问题求解时,不从整体考虑,而是采用一叶障目的选择方式,只选择某种意义上的局部最优解。并且,贪心算法是没有固定的模板可以遵循的,每个题目都有不同的贪心策略,所以算法设计的关键就是贪心策略的选择。
贪心算法有一个必须要注意的事情。贪心算法对于问题的要求是,所有的选择必须是无后效性的,即当前的选择,不能影响后续选择对于结果的影响。
贪心算法主要适用于最优化问题,如:MST
问题。有时候贪心算法并不能得到最优答案,但是能得到精确答案的近似答案。有时可以辅助其他算法得到不是那么精确的结果。
贪心算法的适用范围
符合贪心策略:
所谓贪心选择性质是指所求问题的整体最优解可以通过一系列局部最优的选择,即贪心选择来达到。这是贪心算法可行的第一个基本要素,也是贪心算法与动态规划算法的主要区别。
所谓的贪心选择性质就是,该问题的每一步选择都在选择最优的情况下能够导致最终问题的答案也是最优。
或者说是无后效性,如果该问题的每一步选择都对后续的选择没有影响,就可以是应用贪心算法。
贪心算法的设计步骤
按照定义设计:
1.证明原问题的最优解之一可以由贪心选择得到。
2.将最优化问题转化为这样一个问题,即先做出选择,再解决剩下的一个子问题。
3.对每一子问题一一求解,得到子问题的局部最优解;
4.把子问题的解局部最优解合成原来解问题的一个解
贪心算法的题目讲解
题目如下:
(第一题)
假设商店老板需要找零 n 元钱。
钱币的面额有:100 元、50 元、20 元、5 元、1 元、如何找零使得所需钱币的数量最少?
注意:n 可能为 0,也能为几百元(别问,问就是来着里微信提现来了)
解题思路:
假设需要找n1个100、n2个50、n3个20、n4个5、n5个1
如果可以找到大面额的硬币就使用大面额的,这样可以减少使用多张小面额的硬币,因此我们尽可能的先找出n1然后n2.。。。n5
代码:
import java.util.Scanner;
public class One_36 {
static int[] a=new int[]{100,50,20,5,1};
//用来存放每一种纸币的数量
static int[] b=new int[5];
//写change函数来求解
static void Change(int N) {
for (int i = 0; i < 5; i++) {
b[i]=N/a[i];
N=N%a[i];
}
}
public static void main( String []args) {
Scanner sc=new Scanner(System.in);
int N=sc.nextInt();
Change(N);
//输出结果
for (int i = 0; i <5; i++)
System.out.println(a[i]+":"+b[i]);
}
}
(第二题)
小 B 的宿舍楼沿着走廊南北向的两边各有 200 个房间。
如图所示:
[房间1][房间3][房间5][房间7][房间9 ]...[房间399]
----------------------------------------------
走廊
----------------------------------------------
[房间2][房间4][房间6][房间8][房间10]...[房间400]
最近,由于转专业和专业分流的原因,宿舍将迎来新的调整,以便组成新的班级后方便管理。
但是由于走廊狭窄,走廊里只能通过两个搬运的物品(可以同向也可以反向),因此必须指定高效的搬运计划。
老师给了每位同学下达了以下要求,让同学们体现收拾好行李,然后给每位同学 10 分钟的时间搬运。
当房间 i 搬运行李到 j 时,i 与 j 之间的走廊都会被占用,但是可以容纳两个不同同学同时搬运。所以,10 分钟之内同一段走廊最多两个人同时搬运,不重叠的走廊也可以同时搬运。
小 B 的老师是个数学老师,经过运筹学一通计算他得到了最优的搬运计划。
虽然计划不唯一,但是最优值唯一,请问这个最短时间是多少?
解题思路:
可以把走廊看成一段一段的小节,这样可以求出每一节走廊的占有情况。找出占用次数最多的小节,由于每次可以两个同学占用同一小节,所有此时最少的时间就是最多的次数/2,如果有余数+1。将最终的结果乘以10就是最后的所有时间。
样例输入:
输入解法:
输入数据有 T 组测试例,在第一行给出测试例个数 T。
每个测试例的第一行是一个整数 N(1≤N≤200),表示要搬运行李的人数。接下来 N 行,每行两个正整数 s 和 t,表示一个人,将行李是从房间号码 s 移到到房间号码 t。
输入样例:
3
4
10 20
30 40
50 60
70 80
2
1 3
2 200
3
10 100
20 80
30 50
代码:
import java.util.Scanner;
public class One_38{
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
//搬运次数
int N;
//每次搬运的起点和终点
int from, to;
int maxAns = 0;
int T;
T = in.nextInt();
while (T > 0) {
T--;
N = in.nextInt();
int[] move = new int[200];
for (int i = 0; i < N; i++) {
from=in.nextInt();
to=in.nextInt();
//将房间号映射为走廊号
from = (from - 1) / 2;
to = (to - 1) / 2;
//确保from<to
if (from > to) {
int temp = from;
from = to;
to = temp;
}
//统计占用走廊情况,并统计最大值
for (int j = from; j <= to; j++) {
move[j]++;
maxAns = Math.max(maxAns, move[j]);
}
}
if (maxAns % 2 == 1) maxAns = maxAns / 2 + 1;
else maxAns /=2;
System.out.println( maxAns * 10);
}
}
}
(第三题)
小 B 同学呢,想去吃自助餐,但是他是那种比较节俭的的人,既不想浪费食物,又想尽可能吃的贵一点,他于是私下里做了调查。
小蓝餐厅的自助餐有 n 种食材,每种食材都有它的价格。
而且也能估计出每一份的重量,所以他列了一个表格。
红烧牛肉 30元 300g 油闷大虾 8元 5g 四喜丸子 4元 8g 三文鱼 5元 3g 排骨
18元 200g 麻辣兔头 20元 120g 高汤海参 40元 70g 扇贝粉丝 8元 32g 牛排
79元 240g …现在小 B 想知道在他到底最多吃多少钱的菜品。
假设自助餐厅的菜品供应同样的菜品每个人只能取一份。
小B的饭量假设为 C,单位为 g。
现在请你设计一个程序帮助小 B 计算他的最多吃了多少钱。
解题思路:
由于此题是可拆分背包问题,所以我们可以先吃单价最贵的物品,然后按价格依次递减的吃食物。
输入:
20 1000
1 22
2 43
123 214
12 2
123 432
21 223
22 16
77 49
34 78
34 9
43 677
21 34
23 23
12 56
332 56
21 99
123 545
389 33
12 999
23 88
输出
1204.114
代码:
import java.util.Arrays;
import java.util.Comparator;
import java.util.Scanner;
public class One_40 {
static class Food{
public double w;
public double v;
public double aver;
Food() {
w=0;
v=0;
aver=0;
}
}
static class mycmp implements Comparator<Food>{
@Override
public int compare(Food a, Food b) {
if (a.aver > b.aver) return -1;
else return 1;
//助记大于号就是从大到小排序,小于号就是从小到大排序
}
}
static Food foods[]=new Food[1000];
public static void main( String []args) {
Scanner sc=new Scanner(System.in);
int N=sc.nextInt();
double C=sc.nextDouble();
for (int i = 0; i <N; i++) {
foods[i]=new Food();
foods[i].v=sc.nextDouble();
foods[i].w=sc.nextDouble();
foods[i].aver=foods[i].v/foods[i].w;
}
Comparator cmp=new mycmp();
Arrays.sort(foods,0,N,cmp);
double Value=0;
for (int i = 0; i <N&&C!=0; i++) {
if (foods[i].w <= C) {
Value = Value + foods[i].v;
C = C - foods[i].w;
} else {
//直接将剩余的C加入即可
Value = Value + C * foods[i].aver;
C = 0;
}
}
System.out.println(String.format("%.3f",Value));
}
}