Codeforces Round 922 (Div. 2) --- D. Blocking Elements --- 题解 (二分+优先队列/单调队列的优化技巧+dp + 双指针)

目录

D. Blocking Elements

题目大意:

思路解析:

代码实现:


D. Blocking Elements

题目大意:

 

思路解析:

将整个长度n的序列分为多个部分,求每个部分的和与分割点的和的最大值的最小值,那其实可以看作是否有每个部分小于等于m的分割方案,如果找到一个分割方案,说明当前最大值为m,那么可以发现这样定义之后,答案满足单调性,可以使用二分查找最小的m。

dp[i] 表示第i个点作为分割点,且i-n个数部分和满足小于等于m的定义,假设 i+1 <-> j 的和小于等于m,那么上一个划分点可以为j,j-1,.......,i+2,i+1.则dp[i] = Math.min(dp[k] + arr[i], dp[i]), i+1<=k<=j+1,这个又是某连续区间求最小值,可以使用单调队列维护,当前区间最小。

然后通过dp来维护每个点作为分割点的答案,如果能找到一个dp[i]<=m并且1<->i-1的和也小于等于m,则找到了一种分割方法。

代码实现:

import java.io.*;
import java.math.BigInteger;
import java.util.*;


public class A {
    public static void main(String[] args) throws IOException {
        int t = input.nextInt();
        long inf = Long.MAX_VALUE;
        for (int o = 0; o < t; o++) {
            int n = input.nextInt();
            int[] arr = new int[n + 1];
            long sum = 0;
            for (int i = 1; i <= n; i++) {
                arr[i] = input.nextInt();
                sum += arr[i];
            }
            long l = 0;
            long r = sum;
            long ans = 0;
            while (l <= r) {
                long mid = (l + r) >> 1;
                long[] dp = new long[n + 2];
                Arrays.fill(dp, inf);
                dp[n + 1] = 0;
                int q = n + 1;
                long tot = 0;
                LinkedList<Integer> list = new LinkedList<>();
                list.add(n + 1);
                for (int i = n; i >= 1 ; i--) {

                    while (tot > mid){
                        tot -= arr[q - 1];
                        while (!list.isEmpty() && list.getFirst() >= q) list.removeFirst();
                        q--;
                    }
                    dp[i] = dp[list.getFirst()] + arr[i];
                    while (!list.isEmpty() && dp[list.getLast()] >= dp[i]) list.removeLast();
                    list.add(i);
                    tot += arr[i];
                }
                int yes = 0;
                tot = 0;
                for (int i = 1; i <= n; i++) {
                    if (tot <= mid && dp[i] <= mid){
                        yes = 1;
                        break;
                    }
                    tot += arr[i];
                }
                if (yes == 1){
                    ans = mid;
                    r = mid - 1;
                }else{
                    l = mid + 1;
                }
            }
            out.println(ans);
        }

        out.flush();
        out.close();
        br.close();
    }

    static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
    static Input input = new Input(System.in);
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

    static class Input {
        public BufferedReader reader;
        public StringTokenizer tokenizer;

        public Input(InputStream stream) {
            reader = new BufferedReader(new InputStreamReader(stream), 32768);
            tokenizer = null;
        }

        public String next() {
            while (tokenizer == null || !tokenizer.hasMoreTokens()) {
                try {
                    tokenizer = new StringTokenizer(reader.readLine());
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
            return tokenizer.nextToken();
        }

        public String nextLine() {
            String str = null;
            try {
                str = reader.readLine();
            } catch (IOException e) {
                // TODO 自动生成的 catch 块
                e.printStackTrace();
            }
            return str;
        }

        public int nextInt() {
            return Integer.parseInt(next());
        }

        public long nextLong() {
            return Long.parseLong(next());
        }

        public Double nextDouble() {
            return Double.parseDouble(next());
        }

        public BigInteger nextBigInteger() {
            return new BigInteger(next());
        }
    }

}

  • 9
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Studying~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值