牛客周赛 Round 11 解题报告 | 珂学家 | 线性dp+大剪枝


前言


整体评价

T3和round 9的T3重复了,好意外。T4有点意思,比赛中一度不敢下手,然后试试骗分,发现过了。后来才知道,原来元素两两不等,那基本就退化为 O ( n 2 ) O(n^2) O(n2)了。


A. 小美的外卖订单编号

index 1 / index 0的问题

先减1,再加1

java 复制代码
import java.io.BufferedInputStream;
import java.util.Scanner;

public class Main {

    public static void main(String[] args) {
        Scanner sc = new Scanner(new BufferedInputStream(System.in));
        int kase = sc.nextInt();
        while (kase-- > 0) {
            int m = sc.nextInt();
            int x = sc.nextInt();

            int r = (x - 1) % m + 1;
            System.out.println(r);
        }
    }

}
c++ 复制代码
#include <bits/stdc++.h>

using namespace std;

int main() {
    
    int q;
    cin >> q;
    while (q-- > 0) {
        int m, x;
        cin >> m >> x;
        cout << ((x - 1) + m) % m + 1 << endl;
    }
    return 0;
}

B. 小美的数组操作2

模拟就行,最后判定一下

java 复制代码
import java.io.BufferedInputStream;
import java.util.Scanner;

public class Main {

    public static void main(String[] args) {
        Scanner sc = new Scanner(new BufferedInputStream(System.in));
        int kase = sc.nextInt();
        while (kase-- > 0) {
            int n = sc.nextInt(), m = sc.nextInt();
            int[] arr = new int[n];
            for (int i = 0; i < n; i++) {
                arr[i] = sc.nextInt();
            }
            for (int i = 0;i  < m; i++) {
                int u = sc.nextInt() - 1;
                int v = sc.nextInt() - 1;
                arr[u]++;
                arr[v]--;
            }

            boolean f = true;
            int prev = Integer.MIN_VALUE;
            for (int i= 0; i < n; i++) {
                if (prev > arr[i]) f = false;
                prev = arr[i];
            }
            System.out.println(f ? "Yes" : "No");
        }
    }

}

C. 小美的01串翻转

重题了,复制下上份题解

枚举起点,然后线性模拟, 累加0/1开头的最小值代价即可。

java 复制代码
import java.io.BufferedInputStream;
import java.util.Scanner;
 
public class Main {
 
    public static void main(String[] args) {
        Scanner sc = new Scanner(new BufferedInputStream(System.in));
        char[] str = sc.next().toCharArray();
         
        long ans = 0;
        for (int i = 0; i < str.length; i++) {
            int c0 = 0, c1 = 0;
            for (int j = i; j < str.length; j++) {
                if ((j - i) % 2 == 0) {
                   c0 += str[j] == '0' ? 0 : 1;
                   c1 += str[j] == '1' ? 0 : 1;
                } else {
                   c0 += str[j] == '1' ? 0 : 1;
                   c1 += str[j] == '0' ? 0 : 1;
                }
                // 取两者最小值
                ans += Math.min(c0, c1);
            }
        }
        System.out.println(ans);
 
    }
 
}

这个时间复杂度为 O ( n 2 ) O(n^2) O(n2)

那这题是否可以从贡献的角度切入呢?

感觉有的难,因为子数组它是取代价最小的,每个点的贡献值好像是离散的,并不是一个范围区间。


D. 小美的元素删除

这题的切入点

  • 序列两两为倍数
  • 元素互不相等

排序后,后一个元素都是前一个元素的倍数

因为最大数为10^9, 而最小倍数为2

那么序列的长度最多为31

java 复制代码
int x = n - k;
if (x >= 31) {
  System.out.println(0);
  return;
}

我们把删除k个元素,转化为挑选n-k个

那定义 dpis, 以i元素为末尾元素,且前排累计挑选s个 的方案数

同时为数组建图,这样可以减少状态转移的枚举数

这题的难点在于,如果没有元素彼此不相同的约束,那该如何解?

java 复制代码
import java.io.*;
import java.util.*;

public class Main {

    public static void main(String[] args) {
        Scanner sc = new Scanner(new BufferedInputStream(System.in));
        int n = sc.nextInt(), k = sc.nextInt();
        int[] arr = new int[n];
        for (int i = 0; i < n; i++) {
            arr[i] = sc.nextInt();
        }

        int x = n - k;
        if (x >= 31) {
            System.out.println(0);
            return;
        }

        Arrays.sort(arr);
        long[][] opt = new long[n][x + 1];
        long mod = 10_0000_0007;

        // 建图
        List<Integer> []g = new List[n];
        Arrays.setAll(g, t -> new ArrayList<>());
        for (int i = 0; i < n; i++) {
            for (int j = i + 1; j < n; j++) {
                if (arr[j] % arr[i] == 0) {
                    g[i].add(j);
                }
            }
        }

        // dp
        long ans = 0;
        for (int i = 0; i < n; i++) {
            opt[i][1] = 1;
            for (int j = 0; j < x; j++) {
                if (opt[i][j] == 0) continue; // 这个剪枝也很重要
                for (int v: g[i]) {
                    opt[v][j + 1] = (opt[v][j + 1] + opt[i][j]) % mod;
                }
            }
        }

        for (int i = 0; i < n;i++) {
            ans = (ans + opt[i][x]) % mod;
        }
        System.out.println(ans);

    }

}

写在最后

相关推荐
考虑考虑9 小时前
Sentinel安装
java·后端·微服务
轻颂呀9 小时前
约瑟夫环问题
算法
白帽小阳10 小时前
2026前端面试题!(附答案及解析)
javascript·网络·python·安全·web安全·网络安全·护网行动
凤凰院凶涛QAQ10 小时前
《Java版数据结构 & 集合类剖析》栈与队列:“push/pop 是栈的灵魂,offer/poll 是队列的骨架——四组 API,两种人生”
java·开发语言·数据结构
乱写代码10 小时前
Python开发技巧--类型注解Literal
python
卷无止境11 小时前
Python FFI 技术深度解析:ctypes、cffi 与 pybind11 的性能差异与实践挑战
后端·python
科技大视界11 小时前
投资AI项目,传统尽调不够用了——李章虎律师拆解算法、数据、算力三大雷区
人工智能·算法·数据挖掘
郝学胜-神的一滴11 小时前
算法实战:最小k个数——大顶堆的优雅解法
开发语言·数据结构·c++·python·程序人生·算法·排序算法
Irissgwe11 小时前
算法滑动窗口
数据结构·算法