我一直在研究一些USACO黄金级算法问题,我需要有人帮我向我解释这个问题的解决方案。 Here是问题所在,解决方案如下:
解决增量更新问题的第一步通常是在没有增量更新的情况下解决问题,但在这种情况下,它可能导致一个盲目的小巷。对于这个问题的非增量版本有一个非常简单的DP解决方案,但是如何在不到线性的时间内逐步更新它并不是很明显。
相反,我们可以找到一个更容易更新的分而治之的DP解决方案。将范围分成两半,并在每一半中再次解决问题。实际上,我们根据是否允许使用左端点和/或右端点来解决问题的四种变体。鉴于这两部分的结果,我们可以很容易地将它们组合起来形成整个范围的结果。范围在谷仓上形成一个分段树,每个树节点依赖于它的两个子节点,答案保存在树的根部。
鉴于这种树结构,现在我们可以清楚地知道如何在O(log N)时间内进行增量更新:更新会影响树的叶子,而树的叶子又需要更新其O(log N)祖先。
此解决方案中没有代码,我真的不明白如何实现这个想法。如果有人能够更彻底地解释它或告诉我如何编码它,我将非常感激。
编辑:
我理解解决方案现在说的更好,我编写了一个解决方案。但是,该代码仅适用于前两个测试数据。我以为我编写了他们所说的内容,但我一定是犯了错误。这是我的代码(用Java):
import java.io.File;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.util.Scanner;
/**
* Created by jameslennon on 1/19/14.
*/
public class optmilk {
static int n, d;
static int[] m, indx;
static node[] tree;
public static void main(String[] args) throws Exception {
Scanner in = new Scanner(new File("optmilk.in"));
PrintWriter out = new PrintWriter(new FileWriter("optmilk.out"));
n = in.nextInt();
m = new int[n];
indx = new int[n];
d = in.nextInt();
tree = new node[1 << n + 1];
for (int i = 0; i < n; i++) {
m[i] = in.nextInt();
}
construct(1, 0, n - 1);
int r = 0;
for (int i = 0; i < d; i++) {
int a = in.nextInt() - 1, b = in.nextInt();
update(a, b);
m[a] = b;
r += max(tree[1].none, tree[1].both, tree[1].left, tree[1].right);
}
//System.out.println(r);
out.println(r);
out.close();
System.exit(0);
}
private static void update(int a, int b) {
int i = indx[a];
int w = 1;
tree[i].both = b;
i /= 2;
while (true) {
update_node(i, w);
w *= 2;
if (i == 1) break;
i /= 2;
}
}
private static int max(int... a) {
int max = Integer.MIN_VALUE;
for (int b : a) {
max = Math.max(max, b);
}
return max;
}
private static void update_node(int i, int w) {
if (w == 1) {
tree[i].left = tree[2 * i].both;
tree[i].right = tree[2 * i + 1].both;
} else {
tree[i].none = max(tree[2 * i].none + tree[2 * i + 1].none, tree[2 * i].right + tree[2 * i + 1].none, tree[2 * i].none + tree[2 * i + 1].left);
tree[i].right = max(tree[2 * i].none + tree[2 * i + 1].right, tree[2 * i].right + tree[2 * i + 1].right, tree[2 * i].none + tree[2 * i + 1].both);
tree[i].left = max(tree[2 * i].left + tree[2 * i + 1].none, tree[2 * i].both + tree[2 * i + 1].none, tree[2 * i].left + tree[2 * i + 1].left);
tree[i].both = max(tree[2 * i].left + tree[2 * i + 1].right, tree[2 * i].both + tree[2 * i + 1].right, tree[2 * i].left + tree[2 * i + 1].both);
}
}
private static void construct(int i, int a, int b) {
if (b - a == 0) {
indx[a] = i;
tree[i] = new node(0, 0, 0, m[a]);
return;
}
construct(2 * i, a, (a + b) / 2);
construct(2 * i + 1, (a + b) / 2 + 1, b);
int both = max(tree[2 * i].left + tree[2 * i + 1].right, tree[2 * i].both + tree[2 * i + 1].right, tree[2 * i].left + tree[2 * i + 1].both);
if (b - a == 1) both = 0;
tree[i] = new node(max(tree[2 * i].none + tree[2 * i + 1].none, tree[2 * i].right + tree[2 * i + 1].none, tree[2 * i].none + tree[2 * i + 1].left),
max(tree[2 * i].none + tree[2 * i + 1].right, tree[2 * i].right + tree[2 * i + 1].right, tree[2 * i].none + tree[2 * i + 1].both),
max(tree[2 * i].left + tree[2 * i + 1].none, tree[2 * i].both + tree[2 * i + 1].none, tree[2 * i].left + tree[2 * i + 1].left),
both);
}
static class node {
int none, right, left, both;
public node(int n, int r, int l, int b) {
none = n;
right = r;
left = l;
both = b;
}
}
}
答案 0 :(得分:1)
你不明白哪一部分?
第一段说没有更新时有一个简单的解决方案(即如果农民不进行维护,每天提取相同数量的牛奶。这个数量可以用DP计算。)
第二段说有一种分而治之的解决方案,当机器容量发生变化时更容易更新。它还提供了解决方案的概要。