展开F(i,c), F(i,c+ci), F(i,c+2ci), 可以看到一个sliding window,但是入队的时候需要dp[i-1][j]-mci, m是当前ci的倍数。拿到最大值再把mci加回来即可。
一个单调队列是不够的,需要ci个,分别是F(i,c),F(i,c+1),F(i,c+2),....,F(i,c+ci-1)
private static int knapsackDPSlidingWindow(int N, int C, int[] ss, int[] vs, int[] qs) {
int[][] dp = new int[N+1][C+1];
for (int i=1; i<=N; i++) {
int s = ss[i-1], v = vs[i-1], q = qs[i-1];
List<Deque<int[]>> dqs = new ArrayList<>();
List<Integer> ws = new ArrayList<>(); // window start index
List<Integer> we = new ArrayList<>(); // window end index
for (int j=0, k=0; j<=C; j++, k=(k+1)%s) {
if (k == dqs.size()) {
dqs.add(new LinkedList<>());
ws.add(j);
we.add(j);
}
Deque<int[]> dq = dqs.get(k);
if ((we.get(k)-ws.get(k))/s == q) { // slide window already max out
if (dq.size() > 0 && dq.peekFirst()[0] == ws.get(k)) { // if window start already the max, will eject it
dq.removeFirst();
}
ws.set(k, ws.get(k)+s); // increment the window start
}
int m = (j-k)/s;
// try to add the current index
while (!dq.isEmpty() && dq.peekLast()[1] <= dp[i-1][j]-m*v) {
dq.removeLast();
}
dq.addLast(new int[]{j, dp[i-1][j]-m*v});
we.set(k, j);
dp[i][j] = dq.peekFirst()[1]+m*v;
}
}
return dp[N][C];
}
花了点时间 优化了一下代码,其实可以用一个单调队列,然后循环每个slice,只要每次重置这个单调队列就行了
而且不需要二维数组,可以用一维数组,因为之前的最值都保存在了单调队列里面,从左向右每次dp[j]都表示上面的i-1th 的值,拿来用就好了,保留了Deque class没有优化成数组,更直观,下面是优化过的代码,供参考
这个优化的代码比之前写的二进制优化的方法用时还久哇