我想知道我在这里找到的问题:Finding cheapest combination of items with conditions on the selection 我想继续讨论这个因为我发现这非常有趣并且解决方案非常酷,但我完全理解它有问题。所以我想在另一个问题的评论中问这个问题可能超出了范围。
问题在于如何找到以最便宜的方式从卖家n
购买S
金额i
,这些金额在Name Price Units in storage
Supplier #1 17$ 1 Unit
Supplier #2 18$ 3 Units
Supplier #3 23$ 5 Units
项上有不同的价格。为了完整起见,表格如下:
public class Graph2 {
public static class Vendor {
int itemCount = 0;
double price = 0.0;
public Vendor(int itemCount, double price ) {
this.price = price;
this.itemCount = itemCount;
}
}
public static class Combo {
public Combo(int aCount, int bCount, int cCount, double total) {
this.aCount = aCount;
this.bCount = bCount;
this.cCount = cCount;
this.total = total;
}
public int aCount;
public int bCount;
public int cCount;
public double total;
}
public static int counter = 0;
public static void main(String[] args) {
Vendor a = new Vendor(1, 17.0);
Vendor b = new Vendor(3, 18.0);
Vendor c = new Vendor(5, 23.0);
Map<Integer, List<Combo>> comboMap = new HashMap<>();
for(int i_a = 0; i_a <= a.itemCount; i_a++) {
for(int i_b = 0; i_b <= b.itemCount; i_b++) {
for(int i_c = 0; i_c <= c.itemCount; i_c++) {
StringBuffer buf = new StringBuffer();
buf.append("A: ").append(i_a).append(" B: ").append(i_b).append(" C: ").append(i_c);
int totalCount = i_a + i_b + i_c;
List<Combo> combos = comboMap.computeIfAbsent(totalCount, k -> new ArrayList<>());
combos.add(new Combo(i_a, i_b, i_c, i_a * a.price + i_b * b.price + i_c * c.price));
}
}
}
comboMap.entrySet().stream().forEach(e -> {
Integer totalCount = e.getKey();
List<Combo> combos = e.getValue();
combos.forEach( combo -> {
counter++;
StringBuffer buffer = new StringBuffer();
buffer.append("Buying ").append(totalCount).append(" items. ").append("A: ").append(combo.aCount)
.append(" B: ").append(combo.bCount).append(" C: ").append(combo.cCount).append(" Costs: ").append(combo.total);
System.out.println(buffer.toString());
});
});
System.out.println("Combinations: " + counter);
}
}
建议的解决方案(和正确的答案)是使用Dijkstra的算法。我想讨论这个问题的影响和时间复杂性。首先,我创建了一个非常简单的java循环来确定不同组合的不同价格:
// omitting the first X lines for they are not that important
Buying 8 items. A: 1 B: 3 C: 4 Costs: 163.0
Buying 9 items. A: 1 B: 3 C: 5 Costs: 186.0
Combinations: 48
这产生了这样的结果:
S -> A // connect to A
A -> B|C // connect A to B or C
B -> B|C // connect B to B or C
C -> B|C // connect C to B or C
...
因此,对于这个特定问题,遍历所有卖家的物品数量并计算物品成本的任何组合需要48次迭代。或者:a * b * c,其中a,b和c是卖家的相应项目数(48因为我也计算0结果)
现在,转到图表。我知道这是一个最短路问题(或者可以这样建模)。这意味着我们可以有一个代表买入操作的图表,一次一个,然后我们可以使用Dijkstra并通过将每个边缘的价格加到总重量来确定最短路径。
我甚至构造了图形(除了手动添加顶点和创建边缘,这是我停止的那么繁琐)。但这让我想到了这一点:图表的顶点/边缘数是否大于a * b * c?
例如,对于只有1个子图,我会这样做:(我不做完整的,只是为了表明我的想法)。 A,B,C是卖家。 S是来源(没有项目):
from S -> Amount of sellers (3)
from each of the sellers node on level 1 -> Amount of sellers (3 * 3)
This continues until the items count goes to zero I believe, at which point it will be *2
这意味着粗略意味着:
a -> b -> c == c -> b -> a
这对我来说意味着图表将具有计算事物的每种方式的组合(这将反过来导致重复和更高的复杂性)。这是因为 public static void main(String[] args) {
// A 17$ 1 Unit
// B 18$ 3 Units
// C 23$ 5 Units
Graph g = new Graph();
// Nodes
List<Vertex> vertexes = new ArrayList<>();
vertexes.add(v("source", 0, 0.0));
vertexes.addAll(IntStream.range(1, 2).mapToObj(i -> v("A_"+ i, i, 17.0)).collect(Collectors.toList()));
vertexes.addAll(IntStream.range(1, 4).mapToObj(i -> v("B_"+ i, i, 18.0)).collect(Collectors.toList()));
vertexes.addAll(IntStream.range(1, 6).mapToObj(i -> v("C_"+ i, i, 23.0)).collect(Collectors.toList()));
// Sort the nodes by level (e.g. level 1 means buying 1 item from the seller)
Collections.sort(vertexes, new Comparator<Vertex>() {
@Override
public int compare(Vertex o1, Vertex o2) {
return Integer.compare(o1.level, o2.level);
}
});
// connect the vertexes:
for(int i = 0; i< vertexes.size(); i++) {
Vertex s = vertexes.get(i);
for(int j = i+1; j < vertexes.size(); j++) {
Vertex d = vertexes.get(j);
if(d.level == s.level) continue; // same level can't connect as we can not buy 1 and end up with the same count
if(d.level -1 != s.level) break; // level difference > 1 means we would buy 1 and end up with 2 items
Edge e = e(s, d, d.price); // Create the edge from n to n+1 so that we can buy this
g.edges.add(e);
}
}
g.edges.forEach(System.out::println);
g.vertexes.addAll(vertexes);
}
等等。但是,如何在对图形建模时确定这一点和/或我如何处理这个问题呢?我想在我的算法训练中,我从来没有真正构建图形,而是将一个作为我操作的输入。
所以,总结一下,我想知道:
为了完整起见,在Java中,这是我尝试建模图形的方式:
<source> to <A_1> Cost 17.0
<source> to <B_1> Cost 18.0
<source> to <C_1> Cost 23.0
<A_1> to <B_2> Cost 18.0
<A_1> to <C_2> Cost 23.0
<B_1> to <B_2> Cost 18.0
<B_1> to <C_2> Cost 23.0
<C_1> to <B_2> Cost 18.0
<C_1> to <C_2> Cost 23.0
<B_2> to <B_3> Cost 18.0
<B_2> to <C_3> Cost 23.0
<C_2> to <B_3> Cost 18.0
<C_2> to <C_3> Cost 23.0
<B_3> to <C_4> Cost 23.0
<C_3> to <C_4> Cost 23.0
<C_4> to <C_5> Cost 23.0
这将为我打印以下图表:
<?php
function calculateAge($born, $death) {
$born = strpos($born, '-') === false ? $born . '-01-01' : $born;
$death = strpos($death, '-') === false ? $death . '-01-01' : $death;
$d1 = new DateTime($born);
$d2 = new DateTime($death);
return $d2->diff($d1)->y;
}
然而,这是不完整的,它只允许购买所有卖家的最大数量的物品。这就是为什么我认为我需要一个图表,其中包含所有卖家的所有组合,以便我可以确定任何订单及其路径。
我希望我设法写下我的想法/问题,这样他们才符合逻辑:)如果你发现它不清楚请告诉我,我会尽量让它更清楚一点。
答案 0 :(得分:1)
您不需要明确地创建图形,您只需要使用类似于Dijkstra的贪婪原则。换句话说,在每个购买步骤中,您应该从最便宜的供应商处购买。
您也可以将此问题表述为线性编程,尽管在这种情况下不需要。