简单DP【p1934】封印

Description

很久以前,魔界大旱,水井全部干涸,温度也越来越高。为了拯救居民,夜叉族国王龙溟希望能打破神魔之井,进入人界“窃取”水灵珠,以修复大地水脉。可是六界之间皆有封印,神魔之井的封印由蜀山控制,并施有封印。龙溟作为魔界王族,习有穿行之术,可任意穿行至任何留有空隙的位置。然而封印不留有任何空隙! 龙溟无奈之下只能强行破除封印。破除封印必然消耗一定的元气。为了寻找水灵珠,龙溟必须减少体力消耗。他可以在破除封印的同时使用越行术。

神魔之井的封印共有 n 层,每层封印都有一个坚固值。身为魔族的龙溟单独打破一层封印时需要消耗的元气为该层封印的坚固值和封印总层数 n 的平方的乘积; 但他运用越行术时,打破第 i 层到第 j 层封印(i<j)的总元气消耗为第 i, j 层封印的坚固值之和与第 i, j 层之间所有封印层(包括第 i, j 层)的坚固值之和的乘积。同时,为了不惊动蜀山,第 i, j 层封印的坚固值之和必须不大于一个固定值 t(单独打破时该层坚固值可以大于该值) 。

Input

第一行包含两个正整数 n 和 t,按序表示封印层数和题中所述的固定值。

第二行为 n 个正整数\(a_1,\dots a_n\),按序表示第 1 层到第 n 层封印的坚固值。

Output

仅一行,包含一个正整数,表示最小消耗元气。

很明显,这是一道动态规划.

\(f[i]\)代表打通前\(i\)层的最小消耗元气.

考虑我们可以从上一层直接打通,也可以选择之前的某一层打通.

因此很容易想出状态转移方程
\[ f[i]=min(f[i-1]+a[i] \times n ^2 ,f[j-1]+(a[j]+a[i])\times (sum[i]-sum[j-1])) \]
注意\(j\)枚举的范围为\(1\leq j <i\),且其中\(sum[i]\)维护的是从\(1\)\(i\)的前缀和.

代码

#include<cstdio>
#include<cstdlib>
#include<cctype>
#include<iostream>
#define int long long
#define R register
using namespace std;
inline void in(int &x)
{
    int f=1;x=0;char s=getchar();
    while(!isdigit(s)){if(s=='-')f=-1;s=getchar();}
    while(isdigit(s)){x=x*10+s-'0';s=getchar();}
    x*=f;
}
int n,t,a[1008];
int sum[1008],f[1008];
signed main()
{
    in(n),in(t);
    for(R int i=1;i<=n;i++)
        in(a[i]),sum[i]=sum[i-1]+a[i],f[i]=214748364;
    f[1]=a[1]*n*n;
    for(R int i=2;i<=n;i++)
    {
        f[i]=(f[i-1]+a[i]*n*n);
        for(R int k=1;k<i;k++)
        {
            if(a[i]+a[k]<=t)
            f[i]=min(f[i],f[k-1]+(a[i]+a[k])*(sum[i]-sum[k-1]));
        }
    }
    printf("%lld",f[n]);
}

转载于:https://www.cnblogs.com/-guz/p/9818341.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值