牛客周赛 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个

那定义 dp[i][s], 以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);

    }

}

写在最后

相关推荐
小灰灰__4 分钟前
IDEA加载通义灵码插件及使用指南
java·ide·intellij-idea
零意@5 分钟前
ubuntu切换不同版本的python
windows·python·ubuntu
夜雨翦春韭8 分钟前
Java中的动态代理
java·开发语言·aop·动态代理
思忖小下16 分钟前
Python基础学习_01
python
程序媛小果28 分钟前
基于java+SpringBoot+Vue的宠物咖啡馆平台设计与实现
java·vue.js·spring boot
AI街潜水的八角31 分钟前
基于C++的决策树C4.5机器学习算法(不调包)
c++·算法·决策树·机器学习
追风林34 分钟前
mac m1 docker本地部署canal 监听mysql的binglog日志
java·docker·mac
芒果披萨1 小时前
El表达式和JSTL
java·el
q567315231 小时前
在 Bash 中获取 Python 模块变量列
开发语言·python·bash
是萝卜干呀1 小时前
Backend - Python 爬取网页数据并保存在Excel文件中
python·excel·table·xlwt·爬取网页数据