[日常训练] 小Y包汤圆

【问题描述】

小Y最喜欢吃的就是汤圆了!
这一天,小Y的好朋友小Z给小Y送来了n个汤圆……可是这些汤圆居然是没有馅的!小Y觉得如果连续吃很多个没有馅的汤圆简直是莫大的耻辱!他决定把一些汤圆变成有馅的!
因为汤圆的大小形状各不相同,因此把每个汤圆变成有馅的会花费不同的魔力值。小Y希望连续的m个汤圆里至少要有两个有馅的汤圆。小Y才会觉得心安。
小Y觉得这个问题太简单了,因此他想要问问你,你能告诉他使得任意连续m个汤圆里至少有两个带馅汤圆的最小代价吗?

【输入格式】

第一行两个正整数n、m。
第二行n个正整数,第i个正整数表示把第i个汤圆变成有馅的的代价。

【输出格式】

输出最小代价。

【输入输出样例】

ball.in
6 3
1 5 6 2 1 3

ball.out
9

【数据规模】

对于40%的数据,保证2≤m≤n≤100
对于70%的数据,保证2≤n≤1000,2≤m≤100
对于100%的数据,保证2≤n≤10000,2≤m≤100

【分析】动态规划

  • f[i][j][k] 表示在到第 i 个汤圆为止,上一个有馅的为第ij个汤圆,再上一个有馅的为第 ik 个汤圆时的最小代价( j,k 就表示相对于第 i 个汤圆的距离)
  • 则状态转移方程为:
    [1]、f[i][0][j]=Min(f[i1][j1][k]+vali)(其中 vali 表示把第 i 个汤圆变成有馅的的代价)
    表示若把第i个汤圆变为有馅,则此时就变为上一个有馅的为第 i 个汤圆,而再上一个有馅的为第ij个汤圆
    [2]、 f[i][j][k]=Min(f[i1][j1][k1])
    表示若不把第 i 个汤圆变为有馅,则上两个有馅的汤圆相对于第i个汤圆,距离都要增加1
  • 我们可以发现,转移[1]与 k 的值无关,所以我们可以记g[i][j]=Min(f[i][j][k]),在转移[2]中处理出来,以便在转移[1]中节省时间,那么最后的答案 Answer=Min(g[n][j])
  • 另外由于空间问题, f 数组的第一维i要用01滚动实现

【代码】

#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;
typedef long long ll;
const ll Maxn = 1e18;
const int N = 105;
ll g[2][N], f[2][N][N], Ans = Maxn;
int n, m, val;

inline int get()
{
    char ch; int res;
    while ((ch = getchar()) < '0' || ch > '9');
    res = ch - '0';
    while ((ch = getchar()) >= '0' && ch <= '9')
     res = (res << 3) + (res << 1) + ch - '0';
    return res;
}

inline void CkMin(ll &x, const ll &y) {if (x > y) x = y;}

int main()
{
    freopen("ball.in", "r", stdin);
    freopen("ball.out", "w", stdout);
    n = get(); m = get();
    for (int i = 1; i <= n; ++i)
    {
        val = get();
        int now = i & 1, lst = i - 1 & 1;
        for (int j = 0; j < m; ++j) g[now][j] = Maxn;
        for (int j = 1; j < m; ++j)
         CkMin(g[now][0], f[now][0][j] = g[lst][j - 1] + val);
        for (int j = 1; j < m; ++j)
         for (int k = j + 1; k < m; ++k) 
          CkMin(g[now][j], f[now][j][k] = f[lst][j - 1][k - 1]);
    }
    for (int j = 0; j < m; ++j) CkMin(Ans, g[n & 1][j]);
    printf("%I64d\n", Ans);
    fclose(stdin); fclose(stdout);
    return 0;
}
  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值