牛客周赛Round 99(Go语言)

A题 (A.go)

思路总结: 这道题要求判断一个整数中是否包含连续的两个'9'。

核心思路是将输入的整数转换为字符串,然后遍历这个字符串,检查是否存在相邻的两个字符都是'9'。如果找到了,就立即停止遍历并输出"YES";如果遍历完整个字符串都没有找到,则输出"NO"。

Go 复制代码
// ==========================================================
//                    POWERED BY GMJ
//                   2025-07-06 18:26
// ==========================================================
// Github:     https://github.com/Joker-0111-G
// CSDN:       https://blog.csdn.net/dl999666?type=blog
// Gmail:      joker0111gmj@gmail.com
// Author:     USER <joker0111gmj@gmail.com>
// FilePath:   ~/homegmj/GoCode/NK/20250706\A.go
// License:    MIT
// Copyright:  (c) 2025 JOKER. All rights reserved.
// ==========================================================
//    __  __    _  _____ _   _  ____ __  __     _
//    \ \/ /   / \|_   _| | | |/ ___|  \/  |   | |
//     \  /   / _ \ | | | | | | |  _| |\/| |_  | |
//     /  \  / ___ \| | | |_| | |_| | |  | | |_| |
//    /_/\_\/_/   \_\_|  \___/ \____|_|  |_|\___/
// ==========================================================
// Project Description (use | for new lines)

package main

import (
	"fmt"
	"strconv"
)

func mainA() {
	var n int
	fmt.Scan(&n) 

	s := strconv.Itoa(n)

	found99 := false 
	for i := 0; i < len(s)-1; i++ {
		if s[i] == '9' && s[i+1] == '9' {
			found99 = true 
			break       
		}
	}

	if found99 {
		fmt.Println("YES") 
	} else {
		fmt.Println("NO")  
	}
}
B题 (B.go)

思路总结: 题目要求找到输入字符串中ASCII码最大的字符,并输出其ASCII码值。

解法非常直接:初始化一个变量ma为0,用来记录当前遇到的最大ASCII码。然后遍历输入字符串中的每一个字符,将其转换为对应的ASCII整数值。在遍历过程中,如果当前字符的ASCII值大于ma,就更新ma。遍历结束后,ma中存储的就是整个字符串中最大的ASCII码值,直接输出即可。

Go 复制代码
// ==========================================================
//                    POWERED BY GMJ                         
//                   2025-07-06 19:02
// ==========================================================
// Github:     https://github.com/Joker-0111-G
// CSDN:       https://blog.csdn.net/dl999666?type=blog
// Gmail:      joker0111gmj@gmail.com
// Author:     USER <joker0111gmj@gmail.com>
// FilePath:   ~/homegmj/GoCode/NK/20250706\B.go
// License:    MIT
// Copyright:  (c) 2025 JOKER. All rights reserved.
// ==========================================================
//    __  __    _  _____ _   _  ____ __  __     _
//    \ \/ /   / \|_   _| | | |/ ___|  \/  |   | |
//     \  /   / _ \ | | | | | | |  _| |\/| |_  | |
//     /  \  / ___ \| | | |_| | |_| | |  | | |_| |
//    /_/\_\/_/   \_\_|  \___/ \____|_|  |_|\___/
// ==========================================================
// Project Description (use | for new lines)

package main

import (
	"bufio"
	"fmt"
	"os"
)

func mainB() {
	reader := bufio.NewReader(os.Stdin)

	var T int
	fmt.Fscan(reader, &T) 

	for t := 0; t < T; t++ {
		var n int
		fmt.Fscan(reader, &n) 
		var s string
		fmt.Fscan(reader, &s)

		ma := 0 
		for _, char := range s {
			as := int(char)
			if as > ma {
				ma = as
			}
		}
		fmt.Println(ma) 
	}
}
C题 (C.go)

思路总结: 这道题的目标是构造一个由'4'和'8'组成的、总价值为k的最小数字。"4"的价值是1,"8"的价值是2。

为了让构造出的数字最小,位数应该尽可能少。因为'8'的价值(2)是'4'的价值(1)的两倍,所以应该优先使用'8'来减少位数。

具体策略是:

  1. k对2进行整除,商a表示需要多少个'8'。

  2. k对2取模,如果余数为1 (b=true),说明还需要一个价值为1的'4'。

  3. 为了使数字最小,较小的数字'4'应该放在较高的位(即最前面)。所以,如果需要'4',就先在结果中添加一个'4'。

  4. 然后,在后面追加a个'8'。

  5. 特殊情况:如果k=0,价值为0,无法用'4'或'8'表示,但题目可能要求输出一个有效数字,根据代码逻辑,此时输出'1'(可能是题目特定要求或默认值)。

Go 复制代码
// ==========================================================
//                    POWERED BY GMJ                         
//                   2025-07-06 19:04
// ==========================================================
// Github:     https://github.com/Joker-0111-G
// CSDN:       https://blog.csdn.net/dl999666?type=blog
// Gmail:      joker0111gmj@gmail.com
// Author:     USER <joker0111gmj@gmail.com>
// FilePath:   ~/homegmj/GoCode/NK/20250706\C.go
// License:    MIT
// Copyright:  (c) 2025 JOKER. All rights reserved.
// ==========================================================
//    __  __    _  _____ _   _  ____ __  __     _
//    \ \/ /   / \|_   _| | | |/ ___|  \/  |   | |
//     \  /   / _ \ | | | | | | |  _| |\/| |_  | |
//     /  \  / ___ \| | | |_| | |_| | |  | | |_| |
//    /_/\_\/_/   \_\_|  \___/ \____|_|  |_|\___/
// ==========================================================
// Project Description (use | for new lines)


package main

import (
	"bufio"
	"fmt"
	"os"
	"strings"
)

func mainC() {
	reader := bufio.NewReader(os.Stdin)
	writer := bufio.NewWriter(os.Stdout)
	defer writer.Flush() 

	var T int
	fmt.Fscan(reader, &T) 

	for i := 0; i < T; i++ {
		var k int
		fmt.Fscan(reader, &k) 

		if k == 0 {
			fmt.Fprintln(writer, 1) 
			continue
		}
		a := k / 2
		b := (k % 2 == 1)

		var res strings.Builder 
		if b {
			res.WriteByte('4') 
		}

		for j := 0; j < a; j++ {
			res.WriteByte('8')
		}

		fmt.Fprintln(writer, res.String()) 
	}
}
D题 (D.go)

思路总结: 这是一道数学或逻辑推导题。给定xp,需要计算一个结果。

通过分析代码逻辑 a := p / xb := p - a,我们可以推断这可能与某种分配或分组有关。 核心判断在于 p % x == 0

  • 如果 p 能被 x 整除,结果是 2*a - 1。这里的 a 就是 p/x

  • 如果 p 不能被 x 整除,结果是 2 * b。这里的 bp - p/x

这暗示了两种不同的计算场景。在能整除的情况下,可能存在某种合并或特殊状态,导致结果减1。在不能整除的情况下,则是基于p和商a的差值进行计算。解题的关键是理解这两种情况对应的具体数学模型。

Go 复制代码
// ==========================================================
//                    POWERED BY GMJ                         
//                   2025-07-06 19:10
// ==========================================================
// Github:     https://github.com/Joker-0111-G
// CSDN:       https://blog.csdn.net/dl999666?type=blog
// Gmail:      joker0111gmj@gmail.com
// Author:     USER <joker0111gmj@gmail.com>
// FilePath:   ~/homegmj/GoCode/NK/20250706\D.go
// License:    MIT
// Copyright:  (c) 2025 JOKER. All rights reserved.
// ==========================================================
//    __  __    _  _____ _   _  ____ __  __     _
//    \ \/ /   / \|_   _| | | |/ ___|  \/  |   | |
//     \  /   / _ \ | | | | | | |  _| |\/| |_  | |
//     /  \  / ___ \| | | |_| | |_| | |  | | |_| |
//    /_/\_\/_/   \_\_|  \___/ \____|_|  |_|\___/
// ==========================================================
// Project Description (use | for new lines)

package main

import (
	"bufio"
	"fmt"
	"os"
)

func mainD() {
	reader := bufio.NewReader(os.Stdin)
	writer := bufio.NewWriter(os.Stdout)
	defer writer.Flush() 

	var T int
	fmt.Fscan(reader, &T) 

	for i := 0; i < T; i++ {
		var x, p int
		fmt.Fscan(reader, &x, &p) 
		a := p / x 
		b := p - a

		var res int
		if p%x == 0 {
			res = 2*a - 1
		}else{
			res = 2 * b
		}

		fmt.Fprintln(writer, res) 
	}
}
E题 (E.go)

思路总结: 这道题是一道动态规划(DP)问题。目标是求解对一个数组进行操作后,需要移除多少个只出现一次的元素。

问题可以转化为:最多能保留多少个只出现一次的元素。

我们定义DP状态:

  • dp0: 表示处理到第 i 个元素时,不选择第 i 个元素能保留的最多独特元素数量。

  • dp1: 表示处理到第 i 个元素时,选择第 i 个元素(前提是a[i]是独特元素且满足某种条件)能保留的最多独特元素数量。

状态转移方程: 遍历数组 a1n

  1. 计算 next_dp0 (不选当前元素 a[i-1]):

    • 可以从 dp0 (前一个也不选) 转移而来。

    • 也可以从 dp1 (前一个选了) 转移而来,只要满足不冲突的条件 (如 a[i-2] < i)。

    • next_dp0 = max(dp0, dp1_if_valid)

  2. 计算 next_dp1 (选择当前元素 a[i-1]):

    • 首先,a[i-1] 必须是只出现一次的元素。

    • 可以从 dp0 转移:dp0 + 1,需满足条件 (如 i-1 < a[i-1])。

    • 可以从 dp1 转移:dp1 + 1,需满足条件 (如 a[i-2] < a[i-1])。

    • next_dp1 = max(dp0_to_dp1, dp1_to_dp1)

遍历结束后,max(dp0, dp1) 就是最多能保留的独特元素数量 maxLen

最终答案是 总的独特元素数量 - maxLen

Go 复制代码
// ==========================================================
//
//	POWERED BY GMJ
//	2025-07-06 19:18
//
// ==========================================================
// Github:      https://github.com/Joker-0111-G
// CSDN:        https://blog.csdn.net/dl999666?type=blog
// Gmail:       joker0111gmj@gmail.com
// Author:      USER <joker0111gmj@gmail.com>
// FilePath:    ~/homegmj/GoCode/NK/20250706/E_fixed.go
// License:     MIT
// Copyright:   (c) 2025 JOKER. All rights reserved.
// ==========================================================
//
//	__  __    _  _____ _  _  ____ __  __     _
//	\ \/ /   / \|_   _| | | |/ ___|  \/  |   | |
//	 \  /   / _ \ | | | | | | |  _| |\/| |_  | |
//	 /  \  / ___ \| | | |_| | |_| | |  | | |_| |
//	/_/\_\/_/   \_\_|  \___/ \____|_|  |_|\___/
//
// ==========================================================
package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
)

var (
	in     = bufio.NewScanner(os.Stdin)
	out    = bufio.NewWriter(os.Stdout)
	negInf = -1 << 30
)

func init() {
	in.Split(bufio.ScanWords)
}

func readInt() int {
	in.Scan()
	n, _ := strconv.Atoi(in.Text())
	return n
}

func max(a, b int) int {
	if a > b {
		return a
	}
	return b
}

func solve() {
	n := readInt()
	a := make([]int, n)
	freq := make(map[int]int)
	for i := 0; i < n; i++ {
		val := readInt()
		a[i] = val
		freq[val]++
	}

	numTotalUnique := len(freq)
	isUnique := make(map[int]bool)
	for val, count := range freq {
		if count == 1 {
			isUnique[val] = true
		}
	}

	dp0 := 0
	dp1 := negInf

	for i := 1; i <= n; i++ {
		a_i := a[i-1]
		ndp0_from_dp0 := dp0
		ndp0_from_dp1 := negInf
		if i > 1 {
			if a[i-2] < i {
				ndp0_from_dp1 = dp1
			}
		}
		next_dp0 := max(ndp0_from_dp0, ndp0_from_dp1)

		next_dp1 := negInf
		if isUnique[a_i] {
			ndp1_from_dp0 := negInf
			if i-1 < a_i {
				ndp1_from_dp0 = dp0 + 1
			}

			ndp1_from_dp1 := negInf
			if i > 1 {
				if a[i-2] < a_i {
					ndp1_from_dp1 = dp1 + 1
				}
			}
			next_dp1 = max(ndp1_from_dp0, ndp1_from_dp1)
		}

		dp0 = next_dp0
		dp1 = next_dp1
	}

	maxLen := max(dp0, dp1)
	fmt.Fprintln(out, numTotalUnique-maxLen)
}

func mainE() {
	defer out.Flush()
	t := readInt()
	for t > 0 {
		solve()
		t--
	}
}
F题 (F.go)

思路总结: 这道题要求在满足特定条件下找到一个最大的整数 h。条件是:n 可以表示为 m 个数 x_i 的和(n = x_1 + ... + x_m),其中每个 x_i 都大于等于 h,并且 x_ih 的按位与(bitwise AND)都为0。

核心思路是贪心 + 位运算检验

  1. 贪心策略 : 从高位到低位(例如从第30位开始)尝试构建 h。我们希望 h 尽可能大,所以优先尝试将高位置为1。

  2. 构建过程:

    • 初始化答案 ans = 0

    • b = 300 循环。

    • 在每一位 b,我们尝试将 ans 的第 b 位置为1,形成一个临时的 h,即 h = ans | (1 << b)

  3. 检验可行性:

    • 将每个 x_i 分解为 h + y_i。因为 x_i & h == 0,所以 x_i >= h 等价于 y_i >= 0y_i & h == 0

    • 原方程 n = sum(x_i) 变为 n = sum(h + y_i) = m*h + sum(y_i)

    • 我们需要检验 rem = n - m*h (剩余部分) 是否能被分配到 my_i 中,同时满足 y_i >= 0y_i & h == 0

    • 这个检验过程由 check(rem, m, h) 函数完成。

  4. check函数逻辑:

    • 该函数通过从高位到低位逐位处理 rem 来判断分配是否可能。

    • 它维护一个 debt(债务)变量。对于 rem 的每一位,如果 h 的对应位是1,那么 y_i 的这一位必须是0,所以 rem 在这一位的1不能被分配,会累积成"债务"传递给更低的位。

    • 如果 h 的对应位是0,那么 y_i 的这一位可以是1,我们可以用这m个数来"偿还"债务。最多可以偿还 m

    • 如果在任何时候债务过大(溢出风险),或者到最后债务没有清零,则说明分配失败。

  5. 更新答案:

    • 如果 check 函数返回 true,说明将第 b 位置为1是可行的,我们更新 ans = h

    • 否则,保持 ans 不变,继续尝试下一位。

最终得到的 ans 就是满足条件的最大 h

Go 复制代码
// ==========================================================
//                    POWERED BY GMJ
//                   2025-07-06 19:25
// ==========================================================
// Github:     https://github.com/Joker-0111-G
// CSDN:       https://blog.csdn.net/dl999666?type=blog
// Gmail:      joker0111gmj@gmail.com
// Author:     USER <joker0111gmj@gmail.com>
// FilePath:   ~/homegmj/GoCode/NK/20250706\F.go
// License:    MIT
// Copyright:  (c) 2025 JOKER. All rights reserved.
// ==========================================================
//    __  __    _  _____ _   _  ____ __  __     _
//    \ \/ /   / \|_   _| | | |/ ___|  \/  |   | |
//     \  /   / _ \ | | | | | | |  _| |\/| |_  | |
//     /  \  / ___ \| | | |_| | |_| | |  | | |_| |
//    /_/\_\/_/   \_\_|  \___/ \____|_|  |_|\___/
// ==========================================================
// Project Description (use | for new lines)

package main

import (
	"bufio"
	"fmt"
	"os"
)

// max devuelve el mayor de dos enteros int64.
func max(a, b int64) int64 {
	if a > b {
		return a
	}
	return b
}

// check determina si 'rem' (el resto) se puede escribir como la suma
// de 'm' enteros no negativos, donde cada uno es bitwise ortogonal a 'h'.
func check(rem int64, m int64, h int64) bool {
	var debt int64 = 0 // 'debt' acumula la deuda desde los bits más significativos.

	// Iteramos desde un bit alto para manejar cualquier acarreo de forma segura.
	for j := 62; j >= 0; j-- {
		// Si ya no quedan bits en 'rem' y la deuda es cero, podemos parar.
		if debt == 0 && (rem>>j) == 0 {
			continue
		}

		// Si la deuda es demasiado grande, es imposible que se pague.
		// Esto previene el desbordamiento (overflow) de 'debt'.
		// 1<<62 es una cota segura que está por debajo del límite de int64.
		if debt >= (1 << 62) {
			return false
		}

		// Acumulamos la deuda, desplazándola e incluyendo el bit actual de 'rem'.
		debt = debt*2 + (rem>>j)&1

		// Si el bit j está "permitido" (h_j == 0), podemos usar nuestros 'm'
		// créditos para pagar la deuda.
		if (h>>j)&1 == 0 {
			debt = max(0, debt-m)
		}
	}

	// Si al final del proceso la deuda es cero, la distribución es posible.
	return debt == 0
}

func mainF() {
	in := bufio.NewReader(os.Stdin)
	out := bufio.NewWriter(os.Stdout)
	defer out.Flush()

	var T int
	fmt.Fscan(in, &T)

	for t := 0; t < T; t++ {
		var n, m int64
		fmt.Fscan(in, &n, &m)

		var ans int64 = 0
		// Estrategia greedy: construir la respuesta desde el bit más significativo (30).
		for b := 30; b >= 0; b-- {
			h := ans | (1 << b)

			// Si m*h > n, es imposible. Usamos n/m para evitar desbordamientos.
			if h > n/m {
				continue
			}

			rem := n - m*h

			// Verificamos si este resto se puede distribuir correctamente.
			if check(rem, m, h) {
				// Si es posible, aceptamos este bit.
				ans = h
			}
		}
		fmt.Fprintln(out, ans)
	}
}
相关推荐
梦想很大很大13 小时前
使用 Go + Gin + Fx 构建工程化后端服务模板(gin-app 实践)
前端·后端·go
lekami_兰18 小时前
MySQL 长事务:藏在业务里的性能 “隐形杀手”
数据库·mysql·go·长事务
却尘1 天前
一篇小白也能看懂的 Go 字符串拼接 & Builder & cap 全家桶
后端·go
ん贤1 天前
一次批量删除引发的死锁,最终我选择不加锁
数据库·安全·go·死锁
mtngt111 天前
AI DDD重构实践
go
Grassto3 天前
12 go.sum 是如何保证依赖安全的?校验机制源码解析
安全·golang·go·哈希算法·go module
Grassto5 天前
11 Go Module 缓存机制详解
开发语言·缓存·golang·go·go module
程序设计实验室6 天前
2025年的最后一天,分享我使用go语言开发的电子书转换工具网站
go
我的golang之路果然有问题6 天前
使用 Hugo + GitHub Pages + PaperMod 主题 + Obsidian 搭建开发博客
golang·go·github·博客·个人开发·个人博客·hugo
啊汉7 天前
古文观芷App搜索方案深度解析:打造极致性能的古文搜索引擎
go·软件随想