优先队列优化dp

4 篇文章 0 订阅

题目一

从仓库到码头移动盒子

  1. 定义
    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<=imaxPort,wj<=wimaxWeight
后面的都是随着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一定不会是最小值了,直接出队就行了。

优化算法:

  1. 首先将不可以的从队首中去除,那么剩下的就是可以的最小的f[j]。
  2. 使用f[j]求出转移后的f[i]
  3. 计算出g[i],从队尾去除大于等于gi的gj,直到队列为空,或者队尾小于他为止。
  4. 最后将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

最大子序列和

  1. 定义dp[i]表示以i结尾的最大子序列和
  2. dp[i] = max(s[i] - s[i - j]) j >= 1 and j <= m
  3. 使用单调队列进行优化,固定了s[i],求固定长度区间的最值。
  4. 队列中保存下标,使用s[i]作为指标,由于求最小值,所以如果队尾大于了当前下标对应的s[i],就不可能再是最小值了。
  5. 长度为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);
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值