题意:
在数轴上有n个村子感染了ebola,你要去治疗他们。具体来说,一开始你在1号村子。每天,如果你在村子x,你可以选择治疗村子x的人,或移动到x-1或x+1。每天结束时,如果村子i没有被治疗过,这个村会有ai人死亡。特别的,如果某天你选择了跨过一个没治疗的村子,那你下一次回头时一定要回来治疗这个村子。就是
去的时候治疗了2和4,那第一次回头后,在治疗1和3前不能再回头。
问最少死亡人数。
n<=3000
ai<=10^9
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<iostream>
#define LL long long
#define N 3100
using namespace std;
LL a[N],s[N],f[N],inf;
int n;
int main()
{
freopen("ebola.in","r",stdin);
freopen("ebola.out","w",stdout);
while(1)
{
scanf("%d",&n);
if(n==0) break;
inf=0;
for(int i=1;i<=n;i++) scanf("%lld",&a[i]),inf+=a[i]*(i-1+i-1);
for(int i=1;i<=n;i++) f[i]=inf,s[i]=s[i-1]+a[i];
for(int r=1;r<=n;r++)
{
LL tmp=0;
for(int l=r;l>=1;l--)
{
tmp+=min(1ll*l*a[l]+s[r]-s[l],3ll*r*a[l]-2ll*l*a[l]);
LL t=3*(r-l)+r-l+1,p=tmp-(s[r]-s[l-1])*l;
if(l>1) t++,p+=s[r]-s[l-1];
f[r]=min(f[r],f[l-1]+p+(s[n]-s[r])*t);
}
}
printf("%lld\n",f[n]);
}
return 0;
}
题解:
答案一定是长这个样子的
我们只要能算出每段的最优代价就行了
对于一段[l,r],他对r后面的部分影响是固定的,直接计算
现在就是要决策对于l,r之间的所有点,他是去的时候治疗还是回的时候治疗
重要的一个性质就是对于i< j,无论j怎么决策,如果i是去时治疗,那就一定会在j之前被治疗;如果是回时治疗,就一定在j之后被治疗。这样就可以用贡献和自身代价表示出花费了。
具体就是,
去时治疗的花费=(i−l)∗ai+sr−si
回时治疗的花费=(3r−2i−l)∗ai
注意两边都是−l∗ai,即决策与l无关。所以枚举右端点O(n2)dp一遍就好。