f
[
i
]
=
m
i
n
(
f
[
j
]
+
g
[
j
]
[
i
]
+
(
s
[
n
]
−
s
[
i
]
)
∗
[
(
i
−
j
)
∗
3
+
(
i
−
j
+
1
)
+
1
]
)
f[i]=min(f[j]+g[j][i]+(s[n]-s[i])*[(i-j)*3+(i-j+1)+1])
f[i]=min(f[j]+g[j][i]+(s[n]−s[i])∗[(i−j)∗3+(i−j+1)+1]),其中
g
[
j
]
[
i
]
g[j][i]
g[j][i]表示在从
j
j
j到
i
i
i再到
j
j
j最后返回
i
i
i的过程中最少的死亡人数,且
j
j
j一开始被经过时并不会救。
那么考虑如何求解
g
[
i
]
[
j
]
g[i][j]
g[i][j]。我们可以设
h
[
i
]
[
j
]
h[i][j]
h[i][j]表示在从抵达
i
i
i这个村庄到救完
[
i
,
j
]
[i,j]
[i,j]中的所有村庄并回到
j
j
j这段时间中最少的死亡人数。
考虑
i
i
i这个点是否在经过时就被救,那么有转移
h
[
i
]
[
j
]
=
h
[
i
+
1
]
[
j
]
+
m
i
n
(
s
[
j
]
−
s
[
i
]
+
a
[
i
]
∗
(
j
−
i
)
∗
3
,
(
s
[
j
]
−
s
[
i
]
)
∗
2
)
h[i][j]=h[i+1][j]+min(s[j]-s[i]+a[i]*(j-i)*3,(s[j]-s[i])*2)
h[i][j]=h[i+1][j]+min(s[j]−s[i]+a[i]∗(j−i)∗3,(s[j]−s[i])∗2)。
求解完
h
[
i
]
[
j
]
h[i][j]
h[i][j]后,那可以自然地求出
g
[
i
]
[
j
]
=
h
[
i
+
1
]
[
j
]
+
s
[
j
]
−
s
[
i
]
+
a
[
i
]
∗
3
∗
(
j
−
i
)
g[i][j]=h[i+1][j]+s[j]-s[i]+a[i]*3*(j-i)
g[i][j]=h[i+1][j]+s[j]−s[i]+a[i]∗3∗(j−i)。
时间复杂度:
O
(
n
2
)
O(n^2)
O(n2)。
代码
#include<bits/stdc++.h>#define ll long longusingnamespace std;template<typename T>voidchkmax(T &x, T y){x = x > y ? x : y;}template<typename T>voidchkmin(T &x, T y){x = x < y ? x : y;}template<typename T>voidread(T &x){
x =0;int f =1;char c =getchar();while(!isdigit(c)){if(c =='-') f =-1; c =getchar();}while(isdigit(c)) x = x *10+ c -'0', c =getchar(); x *= f;}constint N =3010;
ll a[N], f[N], s[N], g[N][N], h[N][N];intmain(){int n;read(n);for(int i =1; i <= n; i++)read(a[i]), s[i]= s[i -1]+ a[i];for(int l =1; l <= n; l++)for(int i =1; i <= n - l +1; i++){int j = i + l -1;
h[i][j]= h[i +1][j]+min(s[j]- s[i]+ a[i]*3*(j - i),(s[j]- s[i])*2);}for(int l =1; l <= n; l++)for(int i =1; i <= n - l +1; i++){int j = i + l -1;
g[i][j]= h[i +1][j]+ s[j]- s[i]+ a[i]*3*(j - i);}memset(f,0x3f,sizeof(f)); f[0]=0;for(int i =1; i <= n; i++)for(int j =1; j <= i; j++)chkmin(f[i], f[j -1]+ g[j][i]+(s[n]- s[i])*((i - j)*3+ i - j +2));
cout << f[n]<<'\n';return0;}