accoders 5080 Problem D吃萝卜

[题目描述]

在一个神奇的国度里,有一只编程兔,它每天都写很多的代码,各种编程语言如pascal、c、c++、java、basic等等它都了如指掌,各种算法也都已经滚瓜乱熟了。小花是它的好朋友,经常和它一起玩耍。

某一天,小花给编程兔送来了很多的萝卜。编程兔很开心,决定把它的萝卜和其它的小兔子一起分享。小花共计送来了n袋萝卜(编号1到n),每袋里面都有一定数量的萝卜。小兔子共计有m只,兔子们都很守规矩,按照编号1到m依次排好领取萝卜,萝卜按照编号从小到大的顺序依次发放(也就是编号小的兔子领取前面的萝卜,编号大的兔子领取后面的萝卜,萝卜一定要分完,不能有剩余),每只兔子都只能领取连续的若干袋萝卜,每只兔子至少领取一袋萝卜,一袋萝卜也只能分给一只兔子,不能分给两只以上的兔子。

编程兔希望萝卜尽量能分的平均一点(否则小兔子们要不开心的^_^),也就是它希望得到萝卜最多数量的兔子的萝卜要最少。这个问题对于编程兔来说很简单,亲爱的同学们,你们会么?

输入

第一行是两个正整数n和m,表示萝卜的袋数和兔子的数量。

第二行是n个正整数,表示每袋萝卜的数量。

输出

输出只有一行一个整数,表示得到萝卜最多的那只兔子最少可以得到的萝卜数量。

样例输入

9 3
1 2 3 4 5 6 7 8 9

样例输出

17

提示

样例1中,第1-5袋萝卜分给第一只兔子,总数是15个萝卜,第6-7袋萝卜分给第二只兔子,总数是13个萝卜,第8-9袋萝卜分给第三只兔子,总数是17个萝卜,萝卜最多的兔子得了17个萝卜,这是最多的兔子得到的最少的情况。如果第1-4袋分给第一只兔子,共计10个萝卜,第5-7袋分给第二只兔子共计18个萝卜,第8-9袋分给第三只兔子,共计17个萝卜,这样最多的兔子得到了18个萝卜,比之前的方案大,所以不是最优。

对于60%的数据,1<=m<=n<=100,每袋萝卜的数量不超过10。

对于100%的数据,1<=m<=n<=100000,每袋萝卜的数量不超过10000。

解题思路

这道题可以使用贪心算法来解决。

首先,我们可以使用一个数组sum来保存从第一袋到第i袋萝卜的总数量。那么从第i袋到第j袋萝卜的总数量就可以表示为sum[j]-sum[i-1]。

接下来,我们可以使用一个二维数组dp来保存子问题的解。其中dp[i][j]表示前i只兔子领取前j袋萝卜时,最多得到的萝卜数量。

对于每一个兔子i,我们就需要找到一袋萝卜j,使得dp[i][j]最小。换句话说,我们需要找到一个分界点k,使得dp[i-1][k] + sum[j]-sum[k]最小,即前i-1只兔子领取前k袋萝卜,第i只兔子领取第k+1袋到第j袋萝卜时,最多得到的萝卜数量最小。

那么我们就可以得到状态转移方程:dp[i][j] = min(dp[i][j], dp[i-1][k] + sum[j]-sum[k]),其中 0<=k<=j-1。

最后,答案就是dp[m][n],即前m只兔子领取前n袋萝卜时,最多得到的萝卜数量最小。

☑答案

#include <bits/stdc++.h>
using namespace std;

int main() {
    int n, m;
    cin >> n >> m;
    vector<int> bags(n+1);
    for (int i = 1; i <= n; i++) {
        cin >> bags[i];
    }
    
    vector<int> sum(n+1);
    sum[0] = 0;
    for (int i = 1; i <= n; i++) {
        sum[i] = sum[i-1] + bags[i];
    }
    
    vector<vector<int>> dp(m+1, vector<int>(n+1, INT_MAX));
    dp[0][0] = 0;
    for (int i = 1; i <= m; i++) {
        for (int j = 1; j <= n; j++) {
            for (int k = 0; k <= j-1; k++) {
                dp[i][j] = min(dp[i][j], dp[i-1][k] + sum[j]-sum[k]);
            }
        }
    }
    
    cout << dp[m][n] << endl;

    return 0;
}
☑so,你学废了吗?

本文章为zovetr小面包有点甜撰写,如有侵权,请及时告知

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值