洛谷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