|
贪婪法,又称贪心法是一种不追求最优解,只希望得到较为满意解的方法。贪婪法一般可以快速得到满意的解,因为它省去了为找最优解要穷尽所有可能而必须耗费的大量时间。
贪婪法常以当前情况为基础作最优选择,而不考虑各种可能的整体情况。
例如平时购物找钱时,为使找回的零钱的硬币数最少,不考虑找零钱的所有各种方案,而是从最大面值的币种开始,按递减的顺序考虑各币种,先尽量用大面值的币种,当不足大面值币种的金额时才去考虑下一种较小面值的币种。这就是在使用贪婪法。这种方法在这里总是最优,是因为银行对其发行的硬币种类和硬币面值的巧妙安排。
如只有面值分别为1、5和11单位的硬币,而希望找回总额为15单位的硬币。按贪婪算法,应找1个11单位面值的硬币和4个1单位面值的硬币,共找回5个硬币。但最优的解应是3个5单位面值的硬币。
【问题】 装箱问题
问题描述:装箱问题可简述如下:设有编号为0、1、…、n-1的n种物品,体积分别为v0、v1、…、vn-1。将这n种物品装到容量都为V的若干箱子里。约定这n种物品的体积均不超过V,即对于0≤i<n,有0<vi≤V。不同的装箱方案所需要的箱子数目可能不同。装箱问题要求使装尽这n种物品的箱子数要少。
对适当大的n,找出所有可能的划分要花费的时间是无法承受的。为此,对装箱问题采用非常简单的近似算法,即贪婪法。该算法依次将物品放到它第一个能放进去的箱子中,该算法虽不能保证找到最优解,但还是能找到非常好的解。不失一般性,设n件物品的体积是按从大到小排好序的,即有v0≥v1≥…≥vn-1。如不满足上述要求,只要先对这n件物品按它们的体积从大到小排序,然后按排序结果对物品重新编号即可。装箱算法简单描述如下:
输入箱子的容积;
输入物品种数n;
按体积从大到小顺序,输入各物品的体积;
预置已用箱子链为空;
预置已用箱子计数器box_count为0;
for (i=0;i<n;i++) {
从已用的第一只箱子开始顺序寻找能放入物品i 的箱子j;
if (已用箱子都不能再放物品i) {
另用一个箱子,并将物品i放入该箱子;
box_count++;
} else
将物品i放入箱子j;
}
}
上述算法能求出需要的箱子数box_count,并能求出各箱子所装物品。但不一定能找到最优解,
例:(北大百练题)
一个工厂制造的产品形状都是长方体,它们的高度都是h,长和宽都相等,一共有六个型号,他们的长宽分别为1*1, 2*2, 3*3, 4*4, 5*5, 6*6。这些产品通常使用一个 6*6*h 的长方体包裹包装然后邮寄给客户。因为邮费很贵,所以工厂要想方设法的减小每个订单运送时的包裹数量。他们很需要有一个好的程序帮他们解决这个问题从而节省费用。现在这个程序由你来设计。
输入
输入文件包括几行,每一行代表一个订单。每个订单里的一行包括六个整数,中间用空格隔开,分别为1*1至6*6这六种产品的数量。输入文件将以6个0组成的一行结尾。
输出
除了输入的最后一行6个0以外,输入文件里每一行对应着输出文件的一行,每一行输出一个整数代表对应的订单所需的最小包裹数。
样例输入
0 0 4 0 0 1
7 5 1 0 0 0
0 0 0 0 0 0
样例输出
2
1
程序:
import java.io.BufferedInputStream;
import java.util.Scanner;
/**
* 分情况考虑贪心。关键是3*3的盒子怎么放。
* 6*6的放满整个盒子,
* 5*5的还可以放11个1*1。
* 4*4的还可以放5个2*2的。
* 3*3的总个数模4后,也就是说最后一个盒子放几个3*3的。
* 如果是1,还可以放5个2*2和7个1*1。
* 如果是2,可以放3个2*2和6个1*1。
* 如果是3,可以放1个2*2和5个1*1的。
* 2*2的模9就是最后一个箱子里放2*2的个数,36-(2*2的个数%9)就是可以放1*1的个数。
* 最后(1*1的个数+35)/36就是最后的盒子数。
* @author NC
*/
public class Main {
public static void main(String[] args) {
Scanner scan = new Scanner(new BufferedInputStream(System.in));
while (scan.hasNext()) {
int a1 = scan.nextInt();
int a2 = scan.nextInt();
int a3 = scan.nextInt();
int a4 = scan.nextInt();
int a5 = scan.nextInt();
int a6 = scan.nextInt();
if (a1 == 0 && a2 == 0 && a3 == 0 && a4 == 0 && a5 == 0 && a6 == 0) {
break;
}
int count = 0;
if (a6 > 0) {
count += a6;
}
if (a5 > 0) {
count += a5;
a1 = a1 - a5 * 11;
}
while (a4 > 0) {
count++;
if (a2 >= 5) {
a2 -= 5;
} else {
a1 = a1 - (5 - a2) * 4;
a2 = 0;
}
a4--;
}
count += a3 / 4;
a3 = a3 % 4;
if (a3 > 0) {
count++;
if (a3 == 1) {
if (a2 >= 5) {
a2 -= 5;
a1 -= 7;
} else {
a1 = a1 - (5 - a2) * 4 - 7;
a2 = 0;
}
} else if (a3 == 2) {
if (a2 >= 3) {
a2 -= 3;
a1 -= 6;
} else {
a1 = a1 - (3 - a2) * 4 - 6;
a2 = 0;
}
} else if (a3 == 3) {
if (a2 >= 1) {
a2 -= 1;
a1 -= 5;
} else {
a1 = a1 - 4 - 5;
a2 = 0;
}
}
}
if (a2 > 0) {
count += a2 / 9;//最初时,犯了个超低级错误。。。。36\(2*2)=18
a2 = a2 % 9;
if (a2 > 0) {
count++;
a1 = a1 - 4 * (9 - a2);
}
}
if (a1 > 0) {
count += a1 / 36;
a1 = a1 % 36;
if (a1 > 0) {
count++;
}
}
System.out.println(count);
}
}
} |
|