1 条题解
-
0
自动搬运
来自洛谷,原作者为

Su_Zipei
与卿再世相逢日,玉树临风一少年搬运于
2025-08-24 22:14:16,当前版本为作者最后更新于2020-04-14 07:55:17,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
_ _ _ _# 原题链接:https://www.luogu.com.cn/problem/P5774
分析
直接看这道题,第一个困惑点,那个绝对值的比较是什么东西,根据数学知识,我们可以知道这个意思是到的距离小于到的距离,而路线是线性的,这就意味着当且仅当在的左边时才成立,不然总会有,还不理解?看下图
如果在的位置,那么一定大于吧,所以这个题的题意是只要从往回走去治愈,就必须把之前没治愈过的村庄也治愈了。想到这里,状态就差不多出来了,定义表示治愈前i个村庄的最小死亡数,下面考虑状态转移,对于来说,每个村庄它都有两个选择,治愈先去别的再走回来治愈,治愈的话很好弄,主要考虑的就是略过它的情况,这时候如果依次枚举,效率应该是,程序吃不消,的极限数据我们最少也要压到左右,所以接下来考虑优化。
优化其实也挺简单的,主要有一点很恶心,下边再说。(从这里开始默认在的前边,请勿被上图迷惑)我们发现多出来的时间主要是用在了计算略过村庄再回来的死亡人数的计算,所以我们可以先考虑预处理出从到再从到然后又回到i这一过程中最少的死亡数,于是定义含义为上述的来辅助我们的。我还是补一张图吧……把我自己绕懵了
看了这张图我相信你就明白了数组的含义,接下来考虑如何求解数组,初始的话肯定是为0的,所以转移都应该从这个位置开始,即倒序,那么怎么转移呢,接下来就是很恶心的一个地方,计算经过的天数!很多题解里都没写到这个,这里详细计算一下。对于,同样分两种情况讨论,救助或是略过,不管是救助还是略过,都避免不了经过一个区间,就是到,所以这里可以分而治之,把和到这两个分开,的转移中应该需要有,这里又启示我们进行倒序循环,同样,不管救助还是略过,从走到的这一天里,区间到这一段的村庄都会死亡(为村民默哀?)所以答案累加这个可以由前缀和求出,到了点后,到的死亡人数就已经被记在了里,所以可以不用考虑,这是两种情况所共同具有的死亡人数,下面对两种情况分开讨论,如果救治的人,那么区间,的村民就要多死一天,即,不救治呢?因为同样的我们跑路的代价都记录在了里边,所以不救治的代价就是在这段时间里村死亡的人数,你可能问,别的村难道没有死亡的吗?当然可能会有,但我们已经记录了,所以这里不需要再次加入,首先算一下从跑到再跑回来所需要的时间,这里举个例子,从4到5要1天,4到6要2天,4到7要3天,所以显然跑路时村死亡的人是,2是跑了两遍,是刚刚推出来的,是村日死亡人数,那只有这些吗?当然不是,这只是跑路的代价,根据定义和题意,到这些村庄均被治愈且均在略过后被治愈,所以一个村庄一天,一共就是天,于是我们的就有了转移方程 =++(,)
辅助进行转移的方程有了之后我们就可以进行的转移了,没错,下边还有很恶心的算时间。
对于每前个村庄,都不可能直接求出他的最小值,所以要枚举中间点,即我治愈了前个村庄,但是被略过了,所以的治愈是从走到再走回来时才被治愈,这一段的代价就是,治愈前j个的代价为,直接累加答案即可,那么最硬核的东西就是到的这段区间,因为在当前阶段,这段区间内的人是不可能被治愈的,所以一天内的死亡人数是,天数呢?根据我之前所推导的,从到之间反复横跳一来一回一来需要天数,治愈区间到需要时间。
这里不要忽略了一个地方,就是从跑路跑到时还有一天,所以总天数就是,乘上每天死亡的人数就是最后的代价,累加答案。
至此,这道省选题就落下了帷幕……什么?你问我最后转移的时候没考虑略过就是一路向右的情况?怎么可能,当我枚举到的时候,就相当于转移了这种情况,是吧,所以这个算法是没有问题的,时间复杂度大致为可以A掉
::如果你实在看不懂时间怎么算的请拿起笔自己模拟一下吧,很快就能懂,我也尽力了。
#include<iostream> #include<cstring> #define ll long long using namespace std; const int N=3e3+10; ll s[N],g[N][N],dp[N],a[N]; ll Sum(int l,int r){ return s[r]-s[l-1]; } int main(){ int n; cin>>n; for(int i=1;i<=n;i++){ cin>>s[i];a[i]=s[i];s[i]+=s[i-1]; } for(int i=1;i<=n;i++) for(int j=i-1;j;j--) g[i][j]=g[i][j+1]+Sum(j+1,i)+min(3LL*(i-j)*a[j],Sum(j+1,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]+g[i][j+1]+Sum(i+1,n)*((i-(j+1))*3+i-(j+1)+2)); cout<<dp[n]; }
- 1
信息
- ID
- 4785
- 时间
- 1000ms
- 内存
- 500MiB
- 难度
- 5
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者