【备战秋招】每日一题: 2023.03.21-阿里OD机试(第四题)-加1减1

为了更好的阅读体检,可以查看我的算法学习网站
在线评测链接:P1103

题目内容

塔子哥是一个大学生,他喜欢研究各种关于字符串的问题。有一天,他在研究字符串问题时想到了这个问题。

这道题目给定一个长度为 n n n ( n ≤ 1 0 5 n\le 10^5 n105 )的、由数字字符组成的字符串,你可以进行如下操作: 选择一个字符,使得其加 1 1 1 或者减 1 1 1

请注意,你无法对’ 0 0 0’进行减 1 1 1 操作,也无法对’ 9 9 9’进行加 1 1 1 操作。

若干次操作后,你需要使得该字符串存在一个长度为 k k k ( 1 ≤ k ≤ n 1\le k\le n 1kn )的、所有字符都相同的连续子串。请你计算出操作的最小次数。

输入

3 2
1 2 3

输出

1

思路

枚举 + 滑动窗口

很容易想到暴力枚举的方法。先枚举最终这段连续相同的数字是什么,再去枚举每个长度为 k k k的子段。然后再去计算答案。这样复杂度是 O ( 10 n 2 ) O(10n^2) O(10n2)的。

我们发现每个子段的答案可以使用滑动窗口的方法动态维护。即往右移动一格就把出去的值给减去,然后再加上就行。复杂度 O ( 10 n ) O(10n) O(10n)

类似题目推荐

LeetCode

面试题 17.24. 最大子矩阵 - 同样也是暴力的思路

剑指 Offer 48. 最长不含重复字符的子字符串 - 滑动窗口

209. 长度最小的子数组 -滑动窗口

CodeFun2000

P1290 2023.05.11-华为odA卷-第一题-冗余覆盖 -非常推荐,和本题十分十分类似,也是枚举+滑动窗口

P1175 2023.04.08–华为od-第二题-新学校选址 - 暴力枚举

P1088 2023.3.18.10点-美团春招-第二题-k彩色区间 -滑动窗口

P1001 华为od-k优雅阈值 -滑动窗口

P1119 2023.03.26-第三题-数组之和最小值 - 滑动窗口套数论

代码

CPP

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int INT_MAX = 1e9;
int main() {
    int n, k;
    scanf("%d %d", &n, &k);
    int arr[n];
    for(int i = 0; i < n; i++) {
        scanf("%d", &arr[i]);
    }
    int res = INT_MAX;
    int times[n];
    int sum[n + 1];
    // 枚举这一段最后变成什么 , [0 , 9]
    for(int i = 0; i <= 9; i++) {
        // 先求前k个
        for(int j = 0; j < n; j++) {
            times[j] = abs(arr[j] - i);
        }
        // 模拟滑动窗口
        int curMin = INT_MAX;
        for(int m = 1; m <= n; m++) {
            sum[m] = sum[m - 1] + times[m - 1];
            if(m >= k) curMin = min(curMin, sum[m] - sum[m - k]);
        }
        // 更新答案
        res = min(curMin, res);
    }
    printf("%d", res);

    return 0;
}

python

n , k = list(map(int , input().split()))
a = list(map(int , input().split()))
ans = 10000000000
# 枚举最后变成什么
for d in range (10):
	cost = 0
	# 先求前k个
	for i in range (k):
		cost += abs(a[i] - d)
	ans = min (ans , cost)
	# 滑动窗口
	for i in range (k , n):
		cost += abs(a[i] - d)
		cost -= abs(a[i - k] - d)
		ans = min (ans , cost)

print (ans)

Java

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

public class Main{
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int k = sc.nextInt();
        int[] arr = new int[n];
        for(int i = 0; i < n; i++){
            arr[i] = sc.nextInt();
        }
        int res = Integer.MAX_VALUE;
        int[] times = new int[n];
        int[] sum = new int[n + 1];
        // 枚举这一段最后变成什么 , [0 , 9]
        for(int i = 0; i <= 9; i++){
            // 先求前k个
            for(int j = 0; j < n; j++){
                times[j] = Math.abs(arr[j] - i);
            }
            // 模拟滑动窗口
            int curMin = Integer.MAX_VALUE;
            for(int m = 1; m <= n; m++){
                sum[m] = sum[m - 1] + times[m - 1];
                if(m >= k) curMin = Math.min(curMin, sum[m] - sum[m - k]);
            }
            // 更新答案
            res = Math.min(curMin, res);
        }
        System.out.println(res);

    }
}

Go

package main
import(
    "fmt"
)

func main(){    
    var n int
    var k int
    fmt.Scanln(&n, &k)
    arr := make([]int, n)
    for i := range arr{
        fmt.Scanf("%d", &arr[i])
    }
    ans := getResult(n , k , arr)
    fmt.Println(ans)
}

func getResult(n , k int, arr []int) int {  
    ans := 1000000000
    // 枚举这一段最后变成什么 , [0 , 9]
    for num := 0 ; num <= 9 ; num++{
        tmp := 0
        // 先求前k个
        for i := 0 ; i < k ; i++{
            tmp += abs((arr[i] - num))
        }
        ans = min(ans, tmp)
        // 模拟滑动窗口
        for i := k ; i < n ; i++{
            tmp -= abs((arr[i - k] - num))
            tmp += abs((arr[i] - num))
            // 更新答案
            ans = min(ans, tmp)
        }
    }
    return ans
}

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

func abs(a int) int{
    if a < 0{
        return -a
    }
    return a
}

Js

process.stdin.resume();
process.stdin.setEncoding('utf-8');
let input = '';
process.stdin.on('data', (data) => {
    input += data;
    return;
});
process.stdin.on('end', () => {
    const lines = input.trim().split('\n');
    const [n, k] = lines[0].trim().split(' ').map(x => parseInt(x));
    const a = lines[1].trim().split(' ').map(x => parseInt(x));

    let ans = 10000000000;
    // 枚举最后变成什么
    for (let d = 0; d <= 9; d++) {
        let cost = 0;
        // 先求前k个
        for (let i = 0; i < k; i++) {
            cost += Math.abs(a[i] - d);
        }
        ans = Math.min(ans, cost);
        // 滑动窗口
        for (let i = k; i < n; i++) {
            cost += Math.abs(a[i] - d);
            cost -= Math.abs(a[i - k] - d);
            ans = Math.min(ans, cost);
        }
    }

    console.log(ans);
});

题目内容均收集自互联网,如如若此项内容侵犯了原著者的合法权益,可联系我: (CSDN网站注册用户名: 塔子哥学算法) 进行删除。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值