收银员手里有足够多的100元、50元、20元、10元、5元、2元、1元面值的钱,现在要找给顾客57元,问怎样找钱能使钱的张数最少?
那么如果我把钱设计成只有1元、3元、4元面值的,那么可以用贪心吗?
比如要找给顾客6元,如果用贪心。应该先找1个4元的,然后是2个1元的,显然不是最优解。
贪心策略:不从整体最优考虑,而总是做出在某种意义上的局部最优。(总是做出当前来看是最好的选择,或者简单地说每次选择最好的)
目的:最终结果也是整体最优的
特别说明:因其具有不能对所有问题都得到整体最优解的局限,因此必须首先证明使用贪心的整体结果是最优解
两个要素
问题的整体最优解总是可以通过一系列局部最优的选择来达到
1. 第一步局部选择:选择当前局部最优,保证该局部最优包含在整体最优的一系列策略中。
2. 下一步继续选择:整体最优的下一步还可以通过局部最优达到。
实现框架
从某一初始解开始;
while (逼近目标) {
求出可行解的一个元素;
}
由所有解元素组合成问题的解;
举例一:活动选择
一个会议室在一天内有多场活动,每个活动有其开始时间StartTime和结束时间EndTime,如果选择这场活动,那么它就占据了[StartTime, EndTime)时间区间。请问如何选择活动使得在一天里安排的活动最多?
ID | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
StartTime[ID] | 1 | 3 | 0 | 4 | 3 | 5 | 6 | 8 | 8 | 2 | 12 |
EndTime[ID] | 4 | 6 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
表格中的结束时间已经按非递减排序。
想要安排的活动最多,可能会有这样几种思想:
1. 选择开始时间最早的活动;
2. 选择持续时间最短的活动;
3. 选择结束时间最早的活动。
下面给出第1、2种思想的反例:
结论:结束得早,就可以安排更多的活动!
举例二:背包问题
有一个背包,容量是80,如何使背包里的装的东西总价值最大,但是总重量不超过背包容量?
物品 | A | B | C | D |
重量 | 30 | 50 | 10 | 20 |
价值 | 40 | 50 | 40 | 15 |
那么可能会有下面的想法:
1. 每次挑价值最大的;
2. 每次挑重量最轻的;
3. 每次挑单位重量价值最大的。
这些想法对吗?
其实都不对。原因如下:
如果每次挑价值最大的,那么他的重量也可能大。就不能装更多的东西了。
举例:比如背包容量是50,挑选了价值最大的B,那么就不能再取了,但是取C和D更优!
如果每次挑重量最轻的,那么他的价值可能非常小,而重量大的价值可能非常大。同样也不合理。
如果选单位重量价值大的总该对了吧!也不对。如果存在商品的单位重量价值相等,但是受背包容量的限制,也可能不是最优解。
比如:
物品 | A | B | C |
重量 | 45 | 10 | 40 |
价值 | 45 | 10 | 40 |
那么显然选A就不合适了,因此在单位重量价值相等的条件下,还应先选取重量最小的。
举例三:拼数(NOIP 1998提高组)
题目描述Description
设有n个正整数(n≤20),将它们联接成一排,组成一个最大的多位整数。
例如:n=3时,3个整数13,312,343联接成的最大整数为:34331213
又如:n=4时,4个整数7,13,4,246联接成的最大整数为:7424613
输入输出格式Input/output
输入格式:
第一行,一个正整数n。
第二行,n个正整数。
输出格式:
一个正整数,表示最大的整数
输入输出样例 Sampleinput/output
样例测试点#1
输入样例:
3
13
131
343
输出样例:
34313131
说明description
30%的数据, n≤10,每个数<10^3。 50%的数据, n≤100。 100%的数据, n≤1000,每个数<10^200。
分析:如果比较两个整数的大小,把大的数放在前面,小的数放在后面,看起来没有任何问题。但是假如是12和121,那么12112显然就不是最大的数了,而是12121。那么不能用贪心了嘛?非也。因此我们不应该比较两个数的大小,而是比较两个字符串拼接的大小。如果a+b>=b+a,那么就把a排在b之前。实现如下:
import java.util.ArrayList;
import java.util.Scanner;
public class Main {
public static void main(String[] args){
String str = "";
ArrayList<String> array = new ArrayList<String>();
Scanner in = new Scanner(System.in);
int n = in.nextInt();
while(n-- > 0)
{
array.add(in.next());
}
for(int i = 0; i < array.size(); i++)
{
for(int j = i+1; j < array.size(); j++)
{
if((array.get(i)+array.get(j)).compareTo(array.get(j)+array.get(i)) < 0)
{
String tmp = array.get(i);
array.set(i, array.get(j));
array.set(j, tmp);
}
}
}
for(int i = 0; i < array.size(); i++)
{
str += array.get(i);
}
System.out.println(str);
}
}