考虑DP
直接暴力会
O
(
n
3
)
O(n^3)
O(n3)T飞
考虑优化
记前缀和
s
i
=
∑
j
=
1
i
a
j
s_i=\sum_{j=1}^i a_j
si=∑j=1iaj
首先求出走一段
i
→
j
→
i
i\to j\to i
i→j→i死最多的人数
考虑递推,考虑这一段人的两种死法:
(1) 先治头一个村的人,后面的所有人多死两天
(2) 最后来治头一个村的人,后面的人多死一天,头一个村庄的人死
i
×
3
i\times 3
i×3天(走去,治疗,走回)
设
f
i
,
j
f_{i,j}
fi,j表示走一段
i
→
j
→
i
i\to j\to i
i→j→i死最多的人数,有递推方程:
f
j
,
i
+
j
=
f
j
+
1
,
i
+
j
+
min
{
(
s
i
+
j
−
s
i
)
×
2
,
a
j
×
i
×
3
+
(
s
i
+
j
−
s
i
)
}
f_{j,i+j}=f_{j+1,i+j}+\min\{(s_{i+j}-s_i)\times 2 ,a_j\times i\times 3+(s_{i+j}-s_i) \}
fj,i+j=fj+1,i+j+min{(si+j−si)×2,aj×i×3+(si+j−si)}
其中枚举起点
j
j
j与长度
i
i
i,不可枚举终点(枚举终点时前面的量还没有求出来)
然后考虑答案
记
g
i
g_i
gi为从头走到
i
i
i死亡最少的数量
考虑在
[
1
,
i
]
[1,i]
[1,i]间选一个点
j
j
j,其以前的答案求出,考虑其以后的走法;再考虑
[
i
+
1
,
n
]
[i+1,n]
[i+1,n]死的人
g
i
=
min
{
g
j
+
f
j
+
1
,
i
+
(
4
i
−
4
j
−
2
)
×
(
s
n
−
s
i
)
}
g_i=\min\{g_j+f_{j+1,i}+(4i-4j-2)\times (s_n-s_i) \}
gi=min{gj+fj+1,i+(4i−4j−2)×(sn−si)}
这里的
4
i
−
4
j
−
2
4i-4j-2
4i−4j−2:
[
j
+
1
,
i
]
[j+1,i]
[j+1,i]花
3
(
i
−
(
j
+
1
)
)
=
3
i
−
3
j
−
3
3(i-(j+1))=3i-3j-3
3(i−(j+1))=3i−3j−3天,治愈
i
−
(
j
+
1
)
+
1
i-(j+1)+1
i−(j+1)+1天
[
1
,
j
]
[1,j]
[1,j]这一段已经包含在
g
j
g_j
gj里面了,不用算
注意还要再考虑
j
→
j
+
1
j\to j+1
j→j+1这一天,还要
+
1
+1
+1
总共就是
4
i
−
4
j
−
2
4i-4j-2
4i−4j−2天
注意边界
#include<bits/stdc++.h>
using namespace std;
#define in Read()
#define debug puts("qwq")
#define int long long
int in{
int i=0,f=1;char ch=0;
while(!isdigit(ch)&&ch!='-') ch=getchar();
if(ch=='-') ch=getchar(),f=-1;
while(isdigit(ch)) i=(i<<1)+(i<<3)+ch-48,ch=getchar();
return i*f;
}
const int N=3e3+5;
int n,a[N],s[N],f[N][N],g[N];
signed main(){
n=in;
for(int i=1;i<=n;++i) a[i]=in,s[i]=s[i-1]+a[i];
for(int i=1;i<=n-1;++i)
for(int j=1;j<=n-i;++j)
f[j][i+j]=f[j+1][i+j]+min(((s[i+j]-s[j])<<1),a[j]*i*3+s[i+j]-s[j]);
memset(g,0x3f,sizeof g);
g[0]=0;
for(int i=1;i<=n;++i)
for(int j=0;j<i;++j)
g[i]=min(g[i],g[j]+f[j+1][i]+((i<<2)-(j<<2)-2)*(s[n]-s[i]));
printf("%lld\n",g[n]);
return 0;
}