题目一
- 定义
w [ i ] = ∑ k = 1 i w e i g h t [ k ] w[i] = \sum_{k=1}^iweight[k] w[i]=∑k=1iweight[k]
n e g [ i ] = ∑ k = 1 i p o r t ( k ) ! = p o r t ( k + 1 ) neg[i] = \sum_{k=1}^i port(k) != port(k + 1) neg[i]=∑k=1iport(k)!=port(k+1)
f ( i ) 表示运输钱 i 个需要的最少运输次数 f(i) 表示运输钱i个需要的最少运输次数 f(i)表示运输钱i个需要的最少运输次数
f ( i ) = m i n ( f ( j ) + n e g ( i , j + 1 ) + 2 f(i) = min(f(j) + neg(i, j + 1) + 2 f(i)=min(f(j)+neg(i,j+1)+2
j
<
=
i
−
m
a
x
P
o
r
t
,
w
j
<
=
w
i
−
m
a
x
W
e
i
g
h
t
j <= i - maxPort, wj <= wi - maxWeight
j<=i−maxPort,wj<=wi−maxWeight
后面的都是随着i增长而增大的,所以可用的j一定是增大的,且一旦j不行了,后面的就再也不能使用了。使用优先队列存储可用的id,小的id一定先出队列。
令
g
(
i
)
=
f
(
j
)
−
n
e
g
(
j
+
1
)
g(i) = f(j) - neg(j + 1)
g(i)=f(j)−neg(j+1)
对于 j < i来说,j一定比i更先出队列。
如果gi > gj, 由于求的是最小值,并且gj先出队列,那么gj出列之后,gi还可能成为最小值。
如果gi <= gj, 那么gi在队列中的时候,gj一定不是最小值,而gi又出去的晚,所以gj一定不会是最小值了,直接出队就行了。
优化算法:
- 首先将不可以的从队首中去除,那么剩下的就是可以的最小的f[j]。
- 使用f[j]求出转移后的f[i]
- 计算出g[i],从队尾去除大于等于gi的gj,直到队列为空,或者队尾小于他为止。
- 最后将gi放入队列中
写代码的时候注意long long 的使用,注意求前缀和的边界条件。
class Solution {
public:
int boxDelivering(vector<vector<int>>& boxes, int portsCount, int maxBoxes, int maxWeight) {
typedef long long ll;
int n = boxes.size();
vector<ll>f(n + 1, 0);
vector<ll>w(n + 1, 0);
vector<ll>neg(n + 1, 0);
for (int i = 1; i <= n; i++) {
w[i] = w[i - 1] + boxes[i - 1][1];
}
for (int i = 1; i < n; i++) {
neg[i + 1] = neg[i] + (boxes[i - 1][0] != boxes[i][0]);
}
// for (int i = 0; i <= n; i++) {
// printf("%d %d\n", w[i], neg[i]);
// }
deque<int>que;
que.push_back(0); // f(0) = g(0) = 0
for (int i = 1; i <= n; i++) {
while (!que.empty() && (i - que.front() > maxBoxes || w[i] - w[que.front()] > maxWeight))
que.pop_front();
int j = que.front(); // 最小的j
// printf("%d\n", j);
f[i] = f[j] + neg[i] - neg[j + 1] + 2; // 计算出当前的f[i]
ll gi = f[i] - neg[i]; // 计算出当前的g[i]
while (!que.empty() && gi <= f[que.back()] - neg[que.back() + 1]) // 需要当前的g[i] > g[j]
que.pop_back();
que.push_back(i);
}
return f[n];
}
};
题目2
- 定义dp[i]表示以i结尾的最大子序列和
- dp[i] = max(s[i] - s[i - j]) j >= 1 and j <= m
- 使用单调队列进行优化,固定了s[i],求固定长度区间的最值。
- 队列中保存下标,使用s[i]作为指标,由于求最小值,所以如果队尾大于了当前下标对应的s[i],就不可能再是最小值了。
- 长度为j的区间的和为s[i] - s[i - j]
package dp;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Queue;
/**
* @author: Zekun Fu
* @date: 2023/1/1 20:42
* @Description: 最大子序列和
*
*
*/
public class acwing_135 {
public static void main(String[] args) throws Exception {
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
String[] input = in.readLine().split(" ");
int n = Integer.parseInt(input[0]);
int m = Integer.parseInt(input[1]);
String[] input2 = in.readLine().split(" ");
int[] s = new int[n + 1];
s[0] = 0;
for (int i = 1; i <= n; i++) {
s[i] = Integer.parseInt(input2[i - 1]);
s[i] += s[i - 1];
}
int res = Integer.MIN_VALUE;
Deque<Integer> que = new ArrayDeque<>();
for (int i = 0; i <= n; i++) {
while (!que.isEmpty() && que.getFirst() < i - m) que.pollFirst();
if (!que.isEmpty())
res = Math.max(res, s[i] - s[que.getFirst()]);
while (!que.isEmpty() && s[que.getLast()] >= s[i]) que.pollLast();
que.addLast(i);
}
System.out.println(res);
}
}