笔试题碰到,遗忘了,在此再学习记录一下
参考:https://www.jianshu.com/p/50af9094a2ac
输入总金额zje和物品数量num、物品单价price、物品重要权重weight,求最大化价值value(每件物品单价*价值 求和)
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String in = "";
while ((in = br.readLine()) != null && !"".equals(in)) {
String[] temp = in.split(" ");
int zje = Integer.parseInt(temp[0]);
int num = Integer.parseInt(temp[1]);
Map<Integer, String[]> map = new HashMap<Integer, String[]>();
for (int i = 1; i <= num; i++) {
String[] each = br.readLine().split(" ");
map.put(i, each);
}
System.out.println("======最大价值====" + calc(zje, map));
}
}
1、01。每种物品仅有一个
f[i][j]表示 前i件东西在总金额j限制下最大化价值。第i件物品如果放入背包,那么f[i][j] = f[i - 1] [j - i的price] + i的price*weight,如果不放,f[i][j] = f[i-1][j]。两者取大
/**
* 01背包,每种物品放一遍
*
* 可以使用一维数组进行空间优化
* 一点注意,必须从zje开始循环
*
* @param zje
* @param goodsMap
* @return
*/
public static int calc1(int zje, Map<Integer, String[]> goodsMap) {
int[] result = new int[zje + 1];
Set<Integer> keyset = goodsMap.keySet();
for (int key : keyset) {
String[] valueArr = goodsMap.get(key);
int goodsPrice = Integer.parseInt(valueArr[0]);
int goodsWeight = Integer.parseInt(valueArr[1]);
for (int j = zje; j > 0; j--) {
if (j >= goodsPrice) {
result[j] = Math.max(result[j - goodsPrice] + goodsPrice * goodsWeight, result[j]);
}
}
printArr(result);
}
return result[zje];
}
public static void printArr(int[] values) {
for (int i = 0; i < values.length; i++) {
System.out.print(values[i] + " ");
}
System.out.println();
}
测试输入输出
20 3
2 3
5 2
6 4
0 0 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
0 0 6 6 6 10 10 16 16 16 16 16 16 16 16 16 16 16 16 16 16
0 0 6 6 6 10 24 24 30 30 30 34 34 40 40 40 40 40 40 40 40
======最大价值====40
2、完全背包。所有物品不限量,随便放,其他一样
公式一样,代码从前往后循环便可
/**
* 完全背包,每次循环从本次物品单价开始,到最大金额
* @param zje
* @param goodsMap
* @return
*/
public static int calc2(int zje, Map<Integer, String[]> goodsMap) {
int[] result = new int[zje + 1];
Set<Integer> keyset = goodsMap.keySet();
for (int key : keyset) {
String[] valueArr = goodsMap.get(key);
int goodsPrice = Integer.parseInt(valueArr[0]);
int goodsWeight = Integer.parseInt(valueArr[1]);
for (int j = goodsPrice; j <= zje; j++) {
result[j] = Math.max(result[j - goodsPrice] + goodsPrice * goodsWeight, result[j]);
}
printArr(result);
}
return result[zje];
}
测试输入输出
20 3
2 3
5 2
6 4
0 0 6 6 12 12 18 18 24 24 30 30 36 36 42 42 48 48 54 54 60
0 0 6 6 12 12 18 18 24 24 30 30 36 36 42 42 48 48 54 54 60
0 0 6 6 12 12 24 24 30 30 36 36 48 48 54 54 60 60 72 72 78
======最大价值====78
3、除了总金额限制,另增加其他限制
在上边的条件中,物品分主件和附件,买附件之前需要先买主件,每个主件最多有两种附件。输入中每行物品增加一列,0表示此物品为主件,非0则表示为附件,这非0数表示主键的编号
样例输入
4500 12
100 3 0
400 5 0
300 5 0
1400 2 0
500 2 0
800 2 4
1400 5 4
300 5 0
1400 3 8
500 2 0
1800 4 0
440 5 10
代码还得优化,牛客上内存占用多一大截
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String in = "";
while ((in = br.readLine()) != null && !"".equals(in)) {
String[] temp = in.split(" ");
int zje = Integer.parseInt(temp[0]);
int num = Integer.parseInt(temp[1]);
Map<Integer, HashMap<String, Object>> map = new HashMap<Integer, HashMap<String, Object>>();
for (int i = 1; i <= num; i++) {
String[] arr = br.readLine().split(" ");
int goodsPrice = Integer.parseInt(arr[0]);
int goodsWeight = Integer.parseInt(arr[1]);
// 主件type 为0,附件type 为主件的编号(map的key)
int goodsType = Integer.parseInt(arr[2]);
HashMap<String, Object> each = new HashMap<String, Object>();
each.put("goodsPrice", goodsPrice);
each.put("goodsWeight", goodsWeight);
each.put("goodsType", goodsType);
map.put(i, each);
}
System.out.println("======最大价值====" + calc3(zje, map));
}
/**
* 另类背包
*
* 物品增加类型
*
* @param zje
* @param goodsMap
* @return
*/
public static int calc3(int zje, Map<Integer, HashMap<String, Object>> goodsMap) {
// 把主件和附件作为一个整体转化完全背包问题
// 将附件转到主件底下
convertGoodsMap(goodsMap);
int[][] result = new int[goodsMap.size() + 1][zje + 1];
Set<Integer> keyset = goodsMap.keySet();
int i = 1;
for (int key : keyset) {// 全是主件
HashMap<String, Object> eachGood = goodsMap.get(key);
int goodsPrice = (int) eachGood.get("goodsPrice");
int goodsWeight = (int) eachGood.get("goodsWeight");
// 主件type 为0,附件type 为主件的编号(map的key)
int goodsType = (int) eachGood.get("goodsType");
int p1 = 0, p2 = 0, p3 = 0, v1 = 0, v2 = 0, v3 = 0;
if (eachGood.containsKey("subGoods")) {
// 如果有附件,存在两种情况,一种附件和两种附件
// 一种附件只有一种情况(只放主件的不考虑,下边会处理)
// 两种附件,三种情况,一主一副,一主一副,一主二副
ArrayList list = (ArrayList) eachGood.get("subGoods");
if (list.size() == 1) {
HashMap temp = (HashMap)list.get(0);
p1 = (int)temp.get("goodsPrice") + goodsPrice;
v1 = (int)temp.get("goodsWeight") * (int)temp.get("goodsPrice") + goodsWeight * goodsPrice;
} else {
HashMap temp1 = (HashMap)list.get(0);
HashMap temp2 = (HashMap)list.get(1);
p1 = (int)temp1.get("goodsPrice") + goodsPrice;
v1 = (int)temp1.get("goodsWeight") * (int)temp1.get("goodsPrice") + goodsPrice * goodsWeight;
p2 = (int)temp2.get("goodsPrice") + goodsPrice;
v2 = (int)temp2.get("goodsWeight") * (int)temp2.get("goodsPrice") + goodsPrice * goodsWeight;
p3 = (int)temp1.get("goodsPrice")+ (int)temp2.get("goodsPrice") + goodsPrice;
v3 = (int)temp1.get("goodsWeight") * (int)temp1.get("goodsPrice")+ (int)temp2.get("goodsWeight") * (int)temp2.get("goodsPrice") + goodsPrice * goodsWeight;
}
}
for (int j = 1; j <= zje; j++) {
result[i][j] = result[i - 1][j];
// 因为一个主件可以有0 1 2个附件,分别计算
if (j >= goodsPrice) {
result[i][j] = Math.max(result[i - 1][j - goodsPrice] + goodsPrice * goodsWeight, result[i][j]);
}
if (j >= p1 && p1 > 0) {
result[i][j] = Math.max(result[i - 1][j - p1] + v1, result[i][j]);
}
if (j >= p2 && p2 > 0){
result[i][j] = Math.max(result[i - 1][j - p2] + v2, result[i][j]);
}
if (j >= p3 && p3 > 0) {
result[i][j] = Math.max(result[i - 1][j - p3] + v3, result[i][j]);
}
}
i++;
}
return result[goodsMap.size()][zje];
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private static void convertGoodsMap(Map<Integer, HashMap<String, Object>> goodsMap) {
ArrayList<Integer> del = new ArrayList();
Set<Integer> keyset = goodsMap.keySet();
for (int key : keyset) {
HashMap<String, Object> valueMap = goodsMap.get(key);
// 主件type 为0,附件type 为主件的编号(map的key)
int goodsType = (int) valueMap.get("goodsType");
if (goodsType != 0) {
// 如果是附件,把附件放到主键对应的map中,key为subGoods
if (goodsMap.get(goodsType).containsKey("subGoods")) {
((ArrayList)(goodsMap.get(goodsType).get("subGoods"))).add(valueMap);
} else {
ArrayList arrayList = new ArrayList();
arrayList.add(valueMap);
goodsMap.get(goodsType).put("subGoods", arrayList);
}
// 删掉原goodsMap信息
del.add(key);
}
}
for (int i = 0; i < del.size(); i++) {
goodsMap.remove(del.get(i));
}
}
测试输入输出
4500 12
100 3 0
400 5 0
300 5 0
1400 2 0
500 2 0
800 2 4
1400 5 4
300 5 0
1400 3 8
500 2 0
1800 4 0
440 5 10
======最大价值====16700