【备战秋招】每日一题:4月8日美团春招第三题:题面+题目思路 + C++/python/js/Go/java带注释

为了更好的阅读体检,为了更好的阅读体检,,可以查看我的算法学习博客第三题-玩家打包

在线评测链接:P1168

题目内容

塔子哥开的玩具店生意越来越好,每天都有很多客人前来选购玩具。有一天,他接到了一个大单,客户想购买 n 个玩具,并且要求打包成多个玩具袋。塔子哥精心为客户挑选了 n 个玩具,并且将它们编号为 1,2,\dots,n。

然而,塔子哥发现这个订购单还有一个要求:每个玩具袋最多只能装 m 个玩具,并且同一个玩具袋里装的玩具编号必须是连续的。玩具袋的成本与容积成线性关系。

为了解决这个问题,他决定采用样本中点估计的方法来计算玩具袋的容积。具体来说,如果一个玩具袋中装的最大的玩具容积是 u,最小的是 v,那么这个玩具袋的成本就是 k \times floor((u+v)/2) +s,其中 k 是玩具袋中装入玩具的个数,s 是一个常数, floor(x) 是下取整函数,比如 floor(3.8)=3 , floor(2)=2

客户并没有规定玩具袋的数量,但是希望玩具袋的成本越小越好,毕竟买玩具就很贵了。请求出塔子哥打包这 n 个玩具所用的最小花费。

输入描述

第一行三个正整数 n,m,s 。意义如题面所示

第二行 n 个正整数 a_1,a_2,...,a_n ,表示每个玩具的体积。

对于全部数据, 1\le n \le 10^41\le m \le 10^3m \le n1 \le a_i, s \le 10^4

输出描述

输出一个整数,表示打包这 n 个玩具玩具袋的最小成本。

样例

输入

6 4 3
1 4 5 1 4 1

输出

21

样例解释

前三个玩具装成一个玩具袋,后三个玩具装成一个玩具袋。

思路

序列上的动态规划

状态

dp_i表示将前 i 个玩具装入玩具袋中的最小花费。

转移

解题关键:

考虑枚举最后一个玩具袋的玩具个数:1个,2个,...,m个。确定好最后一个玩具袋的情况,我们能够划归成子问题。故状态转移方程为:
dp_i = \max\limits_{j=\max(0,i-m)}^{i-1} dp_{j}+\lfloor\frac{maxv+minv}{2}\rfloor\times (i-j)+s

其中maxv=\max\limits_{k=j}^i a_kminv=\min\limits_{k=j}^i a_k

时间复杂度:O(nm)

类似题目推荐

LeetCode

132. 分割回文串 II - 一样是在序列上枚举最后一段的情况的动态规划

CodeFun2000

P1178 小红书 2023.04.09-春招-第二题-融合试剂

P1142. 网易有道 2023.4.2-研发岗-第一题-最长k阶完美子序列

P1149 2023.03.30-第一题-塔子哥玩游戏

P1151. 拼多多 2023.03.30-第三题-乘积最大的连续子数组

代码

CPP

#include <bits/stdc++.h>
using namespace std;
​
const int N = 10010, M = 1010;
int a[N], n, m, s;
int dp[N];
​
int main()
{
    scanf("%d%d%d", &n, &m, &s);
​
    for (int i = 1; i <= n; ++i) {
        scanf("%d", &a[i]);
    }
​
    // dp[i] 表示将前 i 个玩具打包的最小权值
    memset(dp, 0x3f, sizeof dp);
    dp[0] = 0;
    for (int i = 1; i <= n; ++i) {
        int maxv = a[i], minv = a[i];
        // 枚举最后一段
        for (int j = i - 1; j >= max(0, i - m); --j) {
            dp[i] = min(dp[i], dp[j] + ((minv + maxv) / 2 * (i - j) + s));
            minv = min(minv, a[j]);
            maxv = max(maxv, a[j]);
        }
    }
​
    printf("%d\n", dp[n]);
​
    return 0;
}

python

n, m, s = map(int, input().split(" "))
a = list(map(int, input().split(" ")))
#dp[i] 表示将前 i 个玩具打包的最小权值
dp = [0x3f3f3f3f] * (n + 1)
dp[0] = 0
for i in range(1, n + 1):
    minv, maxv = a[i - 1], a[i - 1]
    # 枚举最后一段
    for j in range(i - 1, max(-1, i - m - 1), -1):
        dp[i] = min(dp[i], dp[j] + ((minv + maxv) // 2 * k + s))
        if j > 0:
            minv = min(minv, a[j - 1])
            maxv = max(maxv, a[j - 1])
​
print(dp[n])

Java

import java.util.*;
​
public class Main {
​
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int m = sc.nextInt();
        int s = sc.nextInt();
​
        int[] a = new int[n + 1];
        for (int i = 1; i <= n; ++i) {
            a[i] = sc.nextInt();
        }
​
    // dp[i] 表示将前 i 个玩具打包的最小权值
        int[] dp = new int[n + 1];
        dp[0] = 0;
        for (int i = 1; i <= n; ++i) dp[i] = 0x3f3f3f3f;    
        
        for (int i = 1; i <= n; ++i) {
            int maxv = a[i], minv = a[i];
            // 枚举最后一段
            for (int j = i - 1; j >= Math.max(0, i - m); --j) {
                dp[i] = Math.min(dp[i], dp[j] + (minv + maxv) / 2 * (i - j) + s);
                minv = Math.min(minv, a[j]);
                maxv = Math.max(maxv, a[j]);
            }
        }
​
        System.out.println(dp[n]);
    }
}

Go

package main
​
import (
    "fmt"
)
​
func main() {
    var n, m, s int
    fmt.Scan(&n, &m, &s)
​
    a := make([]int, n)
    for i := 0; i < n; i++ {
        fmt.Scan(&a[i])
    }
​
    // dp[i] 表示将前 i 个玩具打包的最小权值
    dp := make([]int, n+1)
    for i := range dp {
        dp[i] = 0x3f3f3f3f
    }
    dp[0] = 0
​
    for i := 1; i <= n; i++ {
        minv, maxv := a[i-1], a[i-1]
        // 枚举最后一段
        for j := i - 1; j > max(-1, i-m-1); j-- {
            mid := (minv + maxv) / 2
            dp[i] = min(dp[i], dp[j]+(mid*(i - j))+s)
            if j > 0 {
                minv = min(minv, a[j-1])
                maxv = max(maxv, a[j-1])
            }
        }
    }
    fmt.Println(dp[n])
}
​
func min(a, b int) int {
    if a < b {
        return a
    }
    return b
}
​
func max(a, b int) int {
    if a > b {
        return a
    }
    return b
}

Js

process.stdin.resume();
process.stdin.setEncoding('utf-8');
let input = '';
process.stdin.on('data', (data) => {
    input += data;
});
process.stdin.on('end', () => {
    const lines = input.trim().split('\n');
    const [n, m, s] = lines[0].split(" ").map(Number); // 解构出n,m,s并转为数字类型
    const a = lines[1].split(" ").map(Number); // 将 a 数组转为数字类型
    
    // dp[i] 表示将前 i 个玩具打包的最小权值
    const dp = new Array(n + 1).fill(0x3f3f3f3f);
    dp[0] = 0;
    for(let i = 1; i <= n; i++) {
        let [minv, maxv] = [a[i - 1], a[i - 1]];
        // 枚举最后一个玩具袋的情况
        for(let j = i - 1; j > Math.max(-1, i - m - 1); j--) {
            dp[i] = Math.min(dp[i], dp[j] + ((Math.floor((minv + maxv) / 2)) * (i - j) + s));
            if(j > 0) {
                minv = Math.min(minv, a[j - 1]);
                maxv = Math.max(maxv, a[j - 1]);
            }
        }
    }
​
    console.log(dp[n]); // 输出结果
});
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

塔子哥学算法

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值