这是一个面试笔试题,要求在1个钟之内完成,虽然我花了三天想办法解决了,但是我还是不能够在1个钟之内回想起来写出我的算法,因为算法很复杂,我不禁觉得出这题的公司是不是想招天才。
水果信息:
李子,重量:4,价格:4500
苹果,重量:5,价格:4700
橘子,重量:2,价格:2250
草莓,重量:1,价格:1100
甜瓜,重量:6,价格:4940
菠萝,重量:2,价格:3900
西瓜,重量:6,价格:5800
桃子,重量:3,价格:3700
香蕉,重量:2,价格:3750
梨子,重量:3,价格:3600
我的解决办法是用回溯法。回溯法有什么用?之前我觉得除了能用来解决八皇后问题就没什么用处了。后来在我解决这个面试题的时候,我发现回溯法可以解决排列组合中的组合问题,而这道面试题中要求水果不能重复,正好是一个组合问题。和一般组合不一样的是,一般的组合的每个组合元素个数是固定的,而这道面试题只要重量不超过规定的重量就算是一个组合。
代码:
package test;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
/**
* 背包最大承重为20,算出装满水果后价格最高的组合,水果不能重复
*/
public class Fruit {
private static int maxWeight = 20;
private String name;
private int price;
private int weight;
public Fruit(String name, int weight, int price) {
this.name = name;
this.weight = weight;
this.price = price;
}
public String getName() {
return name;
}
public int getPrice() {
return price;
}
public int getWeight() {
return weight;
}
public static void printMaxPriceFruits(List<Fruit> fruitList) {
System.out.println("最优解为:");
List<List<Fruit>> maxPriceBagLists = best(fruitList);
for (List<Fruit> bagList : maxPriceBagLists) {
for (Fruit fruit : bagList) {
System.out.print(fruit.getName() + " ");
}
int totalWeight = 0;
int totalPrice = 0;
for (Fruit fruit : bagList) {
totalWeight += fruit.getWeight();
totalPrice += fruit.getPrice();
}
System.out.print("weight:" + totalWeight + " " + "price:"
+ totalPrice);
System.out.println();
}
}
/**
* 用回溯法求出价格最高的组合
*
* @param fruitList
* 所有水果的集合
* @return 返回价格最高的组合
*/
public static List<List<Fruit>> best(List<Fruit> fruitList) {
// 初始化剩余可装重量
int remainWeight = maxWeight;
// 用于回溯法的数组
int[] fruitArr = new int[fruitList.size()];
// 初始化水果选择的起点
for (int i = 0; i < fruitArr.length; i++) {
fruitArr[i] = -1;
}
// 存放水果组合的背包集合
List<Fruit> bagList = new ArrayList<Fruit>();
//
List<List<Fruit>> bagLists = new ArrayList<List<Fruit>>();
// 从第一种水果开始选择
int k = 0;
/*
* 当有增加动作,此变量等于false(解锁),当确定组合则为true(加锁,确定组合后会删除最后一个元素继续判断,
* 有可能删除后的组合符合条件,但是这个组合不是最优解,因为这个组合之前可以添加水果)
*/
boolean lock = false;
while (true) {
fruitArr[k] += 1;
// 所有水果都试过都不能继续添加进背包,则确定一个组合
if (fruitArr[k] >= fruitList.size()) {
if (k > 0) {
if (!lock) {
List<Fruit> copyBagList = new ArrayList<Fruit>();
copyBagList.addAll(bagList);
bagLists.add(copyBagList);
// 打印组合
// for (Fruit fruit : bagList) {
// System.out.print(fruit.getName() + " ");
// }
// int totalWeight = 0;
// int totalPrice = 0;
// for (Fruit fruit : bagList) {
// totalWeight += fruit.getWeight();
// totalPrice += fruit.getPrice();
// }
// System.out.print("weight:" + totalWeight + " "
// + "price:" + totalPrice);
// System.out.println();
// ///
lock = true;
}
remainWeight += bagList.get(bagList.size() - 1).getWeight();// 可承重增加
bagList.remove(bagList.size() - 1);// 背包集合去掉最后一个水果
fruitArr[k] = -1;// 回归起点(这里是回溯法的步骤,这个算法可省去这个步骤)
k--;// 目标更新为上一个水果,继续搜索可能性
} else {// 如果回溯到背包第一个水果,且任何水果都尝试过了,则已经遍历所有可能的组合,跳出循环
break;
}
}
// 如果该水果能放入背包
else if (fruitList.get(fruitArr[k]).getWeight() <= remainWeight) {
bagList.add(fruitList.get(fruitArr[k]));// 添加进背包集合
remainWeight -= fruitList.get(fruitArr[k]).getWeight();// 剩余承重
lock = false;
// 目标更新为背包的下一种水果
k++;
// 如果所有水果都能装入,则k++会溢出,说明背包容量大于等于所有水果重量的合直接返回
if (k >= fruitArr.length) {
bagLists.add(bagList);
return bagLists;
}
// 设定下一个水果选择的起点
fruitArr[k] = fruitArr[k - 1];
}
}
return maxPriceBagLists(bagLists);
}
/**
* 计算最优解组合的集合
*
* @param bagLists
* 所有可能的组合
* @return 返回价格最高的组合
*/
public static List<List<Fruit>> maxPriceBagLists(List<List<Fruit>> bagLists) {
List<List<Fruit>> maxPriceBagLists = new ArrayList<List<Fruit>>();
int maxPrice = 0;
for (List<Fruit> bagList : bagLists) {
int totalPrice = -1;
for (Fruit fruit : bagList) {
totalPrice += fruit.getPrice();
}
if (totalPrice == maxPrice) {
maxPriceBagLists.add(bagList);
} else if (totalPrice > maxPrice) {
maxPrice = totalPrice;
maxPriceBagLists.clear();
maxPriceBagLists.add(bagList);
}
}
return maxPriceBagLists;
}
/**
* @param args
*/
public static void main(String[] args) {
Fruit li = new Fruit("李子", 4, 4500);
Fruit ping = new Fruit("苹果", 5, 4700);
Fruit ju = new Fruit("橘子", 2, 2250);
Fruit cao = new Fruit("草莓", 1, 1100);
Fruit tian = new Fruit("甜瓜", 6, 4940);
Fruit bo = new Fruit("菠萝", 2, 3900);
Fruit xi = new Fruit("西瓜", 6, 5800);
Fruit tao = new Fruit("桃子", 3, 3700);
Fruit xiang = new Fruit("香蕉", 2, 3750);
Fruit pear = new Fruit("梨子", 3, 3600);
List<Fruit> fruitList = new LinkedList<Fruit>();
fruitList.add(li);
fruitList.add(ping);
fruitList.add(ju);
fruitList.add(cao);
fruitList.add(tian);
fruitList.add(bo);
fruitList.add(xi);
fruitList.add(tao);
fruitList.add(xiang);
fruitList.add(pear);
Fruit.printMaxPriceFruits(fruitList);
}
}