牛客周赛 Round 15 解题报告 | 珂学家 | 状态DP构造 + 树形DP

news2024/9/29 13:23:59

前言

alt


整体评价

这场T3挺有意思的,只会3维状态DP进行构造。不过这题其实是脑筋急转弯,有规律可循。

T4是经典的树形DP,从比赛来看,T3难于T4.


A. 游游的整数切割

枚举遍历就行,需要满足前后两段其末尾的元素奇偶一致

import java.io.*;
import java.util.*;

public class Main {

    public static void main(String[] args) {
        Scanner sc = new Scanner(new BufferedInputStream(System.in));
        
        char[] str = sc.next().toCharArray();
        
        int n = str.length;
        int ans = 0;
        // 枚举分界点
        for (int i = 0; i < n - 1; i++) {
            // 前后两段的末尾元素,同奇偶
            if ((str[i] - '0') % 2 == (str[n - 1] - '0') % 2) {
                ans++;
            }
        }
        System.out.println(ans);
    }

}

B. 游游的字母串

枚举最终的那个字母

然后计算代价, 然后取最小的代价即可

这里有个绕的地方,就是如何评估 字母 x -> y的代价

p = abs(x - y) 这是两字母的距离

dist = min(p, 26 - p), 因为环状结构,这个是最小的代价

import java.io.*;
import java.util.*;

public class Main {

    public static void main(String[] args) {
        Scanner sc = new Scanner(new BufferedInputStream(System.in));
        char[] str = sc.next().toCharArray();

        int ans = Integer.MAX_VALUE / 10;
        for (int i = 0; i < 26; i++) {
            int tmp = 0;
            for (int j = 0; j < str.length; j++) {
                int p = str[j] - 'a';
                // 环状结构, 两点的距离 min(s, 26 - s), s = abs(i - p)
                tmp += Math.min(26 - Math.abs(i - p), Math.abs(i - p));
            }
            ans = Math.min(ans, tmp);
        }
        System.out.println(ans);

    }

}

C. 游游的问号替换

1. 状态DP构造解

令 opt[i][s1][s2], i表示前i个字符, s1,s2位最后两位字符(0, 2), 其为布尔值,表示该状态可构造/不可构造

转移方程为

opt[i][s1][s2] = opt[i - 1][s0][s1]

满足 s0 * 9 + s1 * 3 + s2 是偶数,且s0!=s1 && s1!= s2

所以这里的状态是3维,转移又要x3,累计复杂度为 O ( 3 3 ∗ n ) O(3^3*n) O(33n)

当然转移的时候,需要引入一个回溯from数组,用于保存转移的源头

当然这题需要特判,n=1的时候

import java.io.*;
import java.util.*;

public class Main {

    static class Solution {

        int n;
        char[] str;

        public Solution(char[] str) {
            this.str = str;
            this.n = str.length;
        }

        private int[] find(boolean[][][] opt) {
            int n = opt.length;
            for (int i = 0; i < 3; i++) {
                for (int j = 0; j < 3; j++) {
                    if (opt[n - 1][i][j]) {
                        return new int[] {i, j};
                    }
                }
            }
            return null;
        }

        private String build(int[] xy, int[][][] from) {
            // 逆序构造解
            int t1 = xy[0], t2 = xy[1];
            StringBuilder sb = new StringBuilder();
            sb.append((char)(t2 + '0'));
            sb.append((char)(t1 + '0'));
            int m = n - 1;
            while (m >= 2) {
                int c2 = t1;
                int c1 = from[m][t1][t2];
                sb.append((char)(c1 + '0'));
                t2 = c2;
                t1 = c1;
                m--;
            }
            return sb.reverse().toString();
        }

        public String solve() {
            // 特判长度1
            if (n == 1) {
                return str[0] == '?' ? "0" : new String(str);
            }

            boolean[][][] opt = new boolean[n][3][3];
            int[][][] from = new int[n][3][3]; // 追踪来源

            // 初始化前两个元素
            for (int i = 0; i < 3; i++) {
                if (str[0] != '?' && str[0] - '0' != i) continue;
                for (int j = 0; j < 3; j++) {
                    if (str[1] != '?' && str[1] - '0' != j) continue;
                    if (i != j) {
                        opt[1][i][j] = true;
                    }
                }
            }

            for (int i = 2; i < n; i++) {
                for (int j = 0; j < 3; j++) {
                    for (int k = 0; k < 3; k++) {
                        if (j == k) continue;
                        if (!opt[i - 1][j][k]) continue;

                        int t = j * 9 + k * 3;
                        for (int d = 0; d < 3; d++) {
                            if (d == k) continue;
                            if (str[i] == '?' || str[i] - '0' == d) {
                                if ((t + d) % 2 == 0) {
                                    opt[i][k][d] = true;
                                    from[i][k][d] = j;
                                }
                            }
                        }
                    }
                }
            }

            // 判断是否存在可行解
            int[] xy = find(opt);
            if (xy == null) {
                // 说明无解
                return "-1";
            }

            return build(xy, from);

        }

    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(new BufferedInputStream(System.in));
        char[] str = sc.next().toCharArray();
        Solution solution = new Solution(str);
        String res = solution.solve();
        System.out.println(res);
    }

}

2. 脑筋急转弯

分类讨论

  • n = 1

任意字符皆可

“0”, “1”, “2”

  • n = 2

只要 str[0] != str[1],

即 “01”, “02”, “10”, “12”, “20”, “21”

  • n = 3

合法的字符串只有如下几种

“020”, “101”, “121”, “202”

  • n > 3

如果要让所有的连续3长度的子串合法,根据n=3的结论,那只能是0,2字符交叉更替的情况

综合以上,就很容易了

import java.io.BufferedInputStream;
import java.util.Scanner;

public class Main {

    static class Solution {

        public String solve(char[] str) {
            // 根据长度,找到候选集
            String[] candidates = listCandidates(str.length);
            for (String s: candidates) {
                // 进行匹配
                if (match(str, s.toCharArray())) {
                    return s;
                }
            }
            // 找不到匹配
            return "-1";
        }

        // 根据长度, 返回候选的字符串
        String[] listCandidates(int n) {
            if (n == 1) {
                return new String[] {"0", "1", "2"};
            } else if (n == 2) {
                return new String[] {"01", "02", "10", "12", "20", "21"};
            } else if (n == 3) {
                return new String[] {"101", "121", "020", "202"};
            } else {
                // 超过3长度的
                // 构造0,2交叉更替的字符串
                StringBuilder sb1 = new StringBuilder();
                StringBuilder sb2 = new StringBuilder();
                for (int j = 0; j < n; j++) {
                    if (j % 2 == 0) {
                        sb1.append("2");
                        sb2.append("0");
                    } else {
                        sb1.append("0");
                        sb2.append("2");
                    }
                }
                return new String[] {sb1.toString(), sb2.toString()};
            }
        }

        // 进行匹配操作
        boolean match(char[] str, char[] template) {
            for (int j = 0; j < str.length; j++) {
                // ? 匹配 0/1/2
                // 要么0,1,2相等
                if (str[j] != '?' && str[j] != template[j]) {
                    return false;
                }
            }
            return true;
        }

    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(new BufferedInputStream(System.in));
        char[] str = sc.next().toCharArray();
        Solution solution = new Solution();
        System.out.println(solution.solve(str));
    }

}


3. DFS构造

为啥DFS构造可以呢?

它不是有1000的深度吗,就不怕TLE吗?

是因为它的分支少,而且不正确的分支路径极短,因此DFS构造是适合不过的。

import java.io.*;
import java.util.*;

public class Main {

    static class Solution {

        public String solve(char[] str) {
            String r = dfs(str, 0);
            return r == null ? "-1" : r;
        }

        String dfs(char[] str, int s) {
            if (s >= str.length) {
                // 找到解了
                return new String(str);
            }

            // 保留原有的值
            char cp = str[s];
            for (int i = 0; i < 3; i++) {
                // 原字符串不是 ?, 需要过滤
                if (cp != '?' && cp - '0' != i) continue;

                str[s] = (char)(i + '0');
                if (s > 0 && str[s] == str[s - 1]) continue;

                if (s <= 1) {
                    String r = dfs(str, s + 1);
                    if (r != null) return r;
                } else {
                    int acc = (str[s - 2] - '0') * 9 + (str[s - 1] - '0') * 3 + (str[s] - '0');
                    if (acc % 2 == 1) continue;
                    String r = dfs(str, s + 1);
                    if (r != null) return r;
                }
            }
            // 恢复原有的值
            str[s] = cp;
            
            // 找不到合法的字符串
            return null;
        }

    }

    public static void main(String[] args) {    
        Scanner sc = new Scanner(new BufferedInputStream(System.in));
        
        char[] str = sc.next().toCharArray();
    
        Solution solution = new Solution();
        System.out.println(solution.solve(str));
    }

}

D. 游游的树上边染红

这边和 打家劫舍3(树形DP版本)还是有区别的, 一个是点不相邻,一个是边不相邻。

其实这题核心

不是节点染色,还是选染边(不能相邻)

因此对于每个节点, 引入两个状态(0:可被选,1:不可被选)

设计状态 dp[u][s], u为节点, s为0,1状态

状态转移

dp[u][0] = 累加和 ( dp[v][0], dp[v][1] ), v为u的子节点

dp[u][1] = max ( dp[u][0] - max(dp[v][0], dp[v][1]) + dp[v][0] + w(u, v) )

因为状态1,它只能选择一个子节点,两者构建一个染红色的边。

import java.io.*;
import java.util.*;

public class Main {

    static class Solution {

        int n;
        List<long[]> []g;

        long[][] dp;

        // 简单的树形DP
        long solve(int n, List<long[]> []g) {
            this.n = n;
            this.g = g;
            dp = new long[n][2];
            dfs(0, -1);
            return Math.max(dp[0][0], dp[0][1]);
        }

        void dfs(int u, int fa) {
            long x1 = 0, x2 = Long.MIN_VALUE;

        
            for (long[] e: g[u]) {
                int v = (int)e[0];
                if (v == fa) continue;

                dfs(v, u);

                x1 += Math.max(dp[v][0], dp[v][1]);
                x2 = Math.max(x2, -Math.max(dp[v][0], dp[v][1]) + dp[v][0] + e[1]);
            }

            dp[u][0] = x1;
            dp[u][1] = x1 + x2;
        }

    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(new BufferedInputStream(System.in));
        int n = sc.nextInt();
        List<long[]> []g = new List[n];
        Arrays.setAll(g, x -> new ArrayList<>());
        for (int i = 0; i < n - 1; i++) {
            int u = sc.nextInt() - 1, v = sc.nextInt() - 1;
            long w = sc.nextInt();
            g[u].add(new long[] {v, w});
            g[v].add(new long[] {u, w});
        }

        Solution solution = new Solution();
        long res = solution.solve(n , g);
        System.out.println(res);
    }

}


写在最后

alt

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1396109.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Docker 与 Linux Cgroups:资源隔离的魔法之旅

这篇文章主要介绍了 Docker 如何利用 Linux 的 Control Groups&#xff08;cgroups&#xff09;实现容器的资源隔离和管理。 最后通过简单 Demo 演示了如何使用 Go 和 cgroups 交互。 如果你对云原生技术充满好奇&#xff0c;想要深入了解更多相关的文章和资讯&#xff0c;欢迎…

Python OpenCV 影像处理:影像二值化

► 前言 本篇将介绍使用OpenCV Python对于图像上的二值化操作&#xff0c;二值化主要用途包括图像分割、物体侦测、文字识别等。这种转换可以帮助检测图像中的物体或特定特征&#xff0c;并提取有用的信息。透过程式码的说明&#xff0c;让各位了解OpenCV Python于图像处理上的…

mysql安装及部署

1.在/usr/local下创建mysql目录 cd /usr/local mkdir /mysql 2.在mysql目录中下载 cd mysql/ wget https://cdn.mysql.com/archives/mysql-8.0/mysql-8.0.34-1.el9.x86_64.rpm-bundle.tar 3.解压 tar xvf mysql-8.0.34-1.el9.x86_64.rpm-bundle.tar 4.安装 dnf localinst…

JS-WebAPIs-元素尺寸与位置(三)

使用场景&#xff1a; 前面案例滚动多少距离&#xff0c;都是我们自己算的&#xff0c;最好是页面滚动到某个元素&#xff0c;就可以做某些事。简单说&#xff0c;就是通过js的方式&#xff0c;得到元素在页面中的位置这样我们可以做&#xff0c;页面滚动到这个位置&#xff0…

137基于matlab的面和线接触的滑块润滑

基于matlab的面和线接触的滑块润滑&#xff0c;基于有限差分法求解面接触滑块润滑的油膜厚度、油膜压力&#xff0c;输出三维可视化结果。程序已调通&#xff0c;可直接运行。 137 matlab油膜压力油膜厚度 (xiaohongshu.com)

商用软件方案的多种交付方式有什么优势?

商用软件方案在交付上&#xff0c;往往存在多种模式&#xff0c;包括SaaS模式、私有化部署、SDK嵌入式等等&#xff0c;SaaS模式讲究一个标准化&#xff0c;旨在最大限度的降低部署成本&#xff0c;但对于一些定制化程度比较高的需求&#xff0c;往往企业仍然需要采用私有化部署…

暴雨信息与英特尔联合发布全球首个全液冷冷板服务器参考设计

科技之家 1 月 19 日消息&#xff0c;据暴雨服务器官方消息&#xff0c;1 月 18 日&#xff0c;暴雨信息与英特尔联合发布全球首个全液冷冷板服务器参考设计&#xff0c;并面向业界开放&#xff0c;推动全液冷冷板解决方案在全球数据中心的大规模部署应用。 基于该参考设计&am…

10个常考的前端手写题,你全都会吗?

前言 &#x1f4eb; 大家好&#xff0c;我是南木元元&#xff0c;热爱技术和分享&#xff0c;欢迎大家交流&#xff0c;一起学习进步&#xff01; &#x1f345; 个人主页&#xff1a;南木元元 今天来分享一下10个常见的JavaScript手写功能。 目录 1.实现new 2.call、apply、…

区域入侵检测AI边缘计算智能分析网关V4如何通过ssh进行服务器远程运维

智能分析网关V4是一款高性能、低功耗的AI边缘计算硬件设备&#xff0c;它采用了BM1684芯片&#xff0c;集成高性能8核ARM A53&#xff0c;主频高达2.3GHz&#xff0c;并且INT8峰值算力高达17.6Tops&#xff0c;FB32高精度算力达到2.2T&#xff0c;每个摄像头可同时配置3种算法&…

SD-WAN保障服务质量的五大核心功能

基于 MPLS 的传统专用网络极度结构化、僵化&#xff0c;缺乏灵活性和变通性。SD-WAN让传统的MPLS专用网络显得相形见绌。这种创新的网络架构不仅在管理灵活性和成本效益方面取得显著进展&#xff0c;更能够随着业务的扩张和新分支机构的增设而高效扩展。在SD-WAN中&#xff0c;…

Linux操作系统----gdb调试工具(配实操图)

绪论​ “不用滞留采花保存&#xff0c;只管往前走去&#xff0c;一路上百花自会盛开。 ——泰戈尔”。本章是Linux工具篇的最后一章。gdb调试工具是我们日常工作中需要掌握的一项重要技能我们需要基本的掌握release和debug的区别以及gdb的调试方法的指令。下一章我们将进入真正…

探索设计模式的魅力:“感受单例模式的力量与神秘” - 掌握编程的王牌技巧

在软件开发的赛场上&#xff0c;单例模式以其独特的魅力长期占据着重要的地位。作为设计模式中的一员&#xff0c;它在整个软件工程的棋盘上扮演着关键性角色。本文将带你深入探索单例模式的神秘面纱&#xff0c;从历史渊源到现代应用&#xff0c;从基础实现到高级技巧&#xf…

数据结构实验6:图的应用

目录 一、实验目的 1. 邻接矩阵 2. 邻接矩阵表示图的结构定义 3. 图的初始化 4. 边的添加 5. 边的删除 6. Dijkstra算法 三、实验内容 实验内容 代码 截图 分析 一、实验目的 1&#xff0e;掌握图的邻接矩阵的存储定义&#xff1b; 2&#xff0e;掌握图的最短路径…

Idea 开发环境不断切换git代码分支导致冲掉别人代码

问题分析 使用git reflog查看执行命令&#xff0c;以下是发生事故的切换和提交动作 46f72622e1 HEAD{41}: commit: feat: 【Sales - 6.3】小程序端不登录也可以录入客户线索 c5e7d9f6e1 HEAD{42}: fetch origin feature/20240102_Sales6.3_xingang:feature/20240102_Sales6.3…

基于 IDEA 进行 Maven 依赖管理

一、依赖管理概念 Maven 依赖管理是 Maven 软件中最重要的功能之一。Maven 的依赖管理能够帮助开发人员自动解决软件包依赖问题&#xff0c;使得开发人员能够轻松地将其他开发人员开发的模块或第三方框架集成到自己的应用程序或模块中&#xff0c;避免出现版本冲突和依赖缺失等…

旧衣物回收小程序搭建,旧衣回收利润有多高?

在当下人们的生活水平日益提高&#xff0c;淘汰的旧衣物逐渐增加。面对这些旧衣物&#xff0c;很多人舍不得丢弃&#xff0c;既浪费又污染环境&#xff0c;只能常年堆积在家里。为了解决这一问题&#xff0c;旧衣回收成为了大众的选择。大家能够将淘汰的衣物进行回收&#xff0…

Java-NIO篇章(4)——Selector选择器详解

Selector介绍 选择器&#xff08;Selector&#xff09;是什么呢&#xff1f;选择器和通道的关系又是什么&#xff1f;这里详细说明&#xff0c;假设不用选择器&#xff0c;那么一个客户端请求数据传输那就需要建立一个连接&#xff0c;为了避免线程阻塞&#xff0c;那么每个客…

微分方程(1)微分方程的历史

总之就是学习吧~ Introduction Without knowing something about differential equations and methods of solving them, it is difficult to appreciate the history of this important branch of mathematics. Further, the development of differential equations is inti…

如何用GOWIN创建FPGA工程

高云FPGA如何创建工程 第一步&#xff1a;安装Gowin软件&#xff0c;这个在高云的官网是可以下载的 第二步&#xff1a;点开这个软件&#xff0c;点击“New Project…” 点击“Next” 选择自己对应的器件型号&#xff1a; 工程创建成功&#xff0c;如下图&#xff1a; 最后将…

给程序加个进度条吧!1行Python代码,快速搞定~

你在写代码的过程中&#xff0c;有没有遇到过以下问题&#xff1f; 已经写好的程序&#xff0c;想看看程序执行的进度&#xff1f; 在写代码批量处理文件的时候&#xff0c;如何显示现在处理到第几个文件了&#xff1f; &#x1f446;如上图所示的进度条是一个最好的解决方法…