其他分享
首页 > 其他分享> > 洛谷P5774,可爱的动态规划。

洛谷P5774,可爱的动态规划。

作者:互联网

如此可爱的动态规划见过么?

  相信各位都非常喜欢动态规划,那我就写一道可爱的动态规划的题解吧。

题目:https://www.luogu.com.cn/problem/P5774

题意:

  题意“挺明白”的。。。题意:给你一个序列每个数表示第i个村庄一天要因感染而死的人数,大佬你能治这种病,花费一天治好一个村庄,而你移动还要花费一天,问治愈好所有村庄最少死的人。限制:只要从i走到i-1,就必须把i之前的所有村庄全部治愈。

分析:

  这题很容易看出是动态规划,但是,怎么规划呢(要求时间效率n方),暴力规划n3,我们并不能很好的解决它,那咋办:单调队列??,好像没啥单调。。。我们只能想别的办法:二分答案??,好像也不是很好判断死多少人可以完成,还是要回到动态规划。

  枚举长度,起点,断点三个是不行的,那我们可不可考虑先维护2个?

  我们先枚举起点和长度:那我们怎么做才能不用枚举断点呢?——加限制:我们定义Dp[i][j]表示治愈好i到j所有人过程中i到j中的死亡的最少人数,并且要求路线是i到j到i,于是,我们就可不枚举断点了,为什么呢:想一想,我把i到j所有村庄治愈以后,我不论怎么走动,这一段也不会再有死亡了,所以我无论最后停在哪里,这个Dp的值不变。

  然后想出转移方程(这个就推导一下就好了,关键是怎么n3变n2

  Dp[i][j]=Dp[i+1][j]+min((sum[j]-sum[i])*2,a[i]*(d-1)*3+sum[j]-sum[i]);

  可是显然Dp[i][n]并不是我们最后想要的结果,因为我们并不一定要1到n到1,中间可以回头。

  不过没关系,我们已经成功一半了,我们再定义一个数组dp[i]表示前i个村庄被治愈之后,1到n总共最少死的人数,你会发现:不用枚举起点,太棒了,只要想出转移方程来就可以了:

  dp[i]=min(dp[j]+Dp[j+1][i]+(4*(i-j)-2)*(sum[n]-sum[i]))(j属于n*,j<i)

  最后,显然Dp[n]就是我们想要的答案了。

  很可爱的动态规划,大家是不是更爱动态规划了呢?

代码:

  又是极度的简单

#include <cstdio>
#include <string>
#include <cstring>
using namespace std;
const int maxn=3000+10;
long long a[maxn],sum[maxn],Dp[maxn][maxn],dp[maxn];
int main(){
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%lld",&a[i]);
        sum[i]=sum[i-1]+a[i];
    }
    for(int d=2;d<=n;d++)
        for(int i=1,j;(j=i+d-1)<=n;i++)
            Dp[i][j]=Dp[i+1][j]+min((sum[j]-sum[i])*(long long)2,a[i]*(long long)(d-1)*(long long)3+sum[j]-sum[i]);
    memset(dp,0x3f,sizeof(dp));
    dp[0]=0;
    for(int i=1;i<=n;i++)
        for(int j=0;j<i;j++)
            dp[i]=min(dp[i],dp[j]+Dp[j+1][i]+((long long)4*(long long)(i-j)-(long long)2)*(sum[n]-sum[i]));
    printf("%lld",dp[n]);
    return 0;
}

没注释,各位大佬应该一看就懂

槽点:

  死亡人数用long long,你在逗我。。。

精华:

  多分析,可尝试动态规划多次:像本题一次区间规划,一次线性规划。

我们是:OIer~~~,我们要:Ac~~~,我们需:加油~~~!!!

标签:洛谷,sum,P5774,枚举,maxn,可爱,动态,规划,Dp
来源: https://www.cnblogs.com/wish-all-ac/p/12615136.html