Souce:2014-2015 Summer Petrozavodsk Camp, Andrew Stankevich Contest 46 (ASC 46)
Problem:
n个村庄,如果第i个村庄没有被治疗,每天会死掉a[i]个人。现在起始在第一个村庄,每次选择花费一天的时间 往前走/往后走/治疗当前村庄,使得总共死去的人数最少。
但行动有个限制,如果你的前进方向上有一个曾经被路过但没有被治疗的村庄,那么直到这个村庄被治疗之前,你都不能改变你的方向,并且当你经过这个村庄时,必须治疗他。
Idea:
d
p
[
l
]
[
r
]
dp[l][r]
dp[l][r]表示当前在l点,一直前进到
r
r
r点再折回到l点所需要的最小花费。答案一定是由数个区间拼接而成的。
对于
l
l
l和
r
r
r都固定时,在治疗村庄时
i
i
i,把后面所有村庄的贡献都归到
i
i
i身上,那么可以得到
i
i
i点的贡献为
m
i
n
(
(
i
−
l
)
∗
a
[
i
]
+
s
[
r
]
−
s
[
i
]
,
(
3
∗
r
−
2
∗
i
−
l
)
∗
a
[
i
]
)
min((i-l)*a[i]+s[r]-s[i], (3*r-2*i-l)*a[i])
min((i−l)∗a[i]+s[r]−s[i],(3∗r−2∗i−l)∗a[i])。
注意到抠出式子中的
−
l
∗
a
[
i
]
-l*a[i]
−l∗a[i]可以使DP优化到
O
(
N
2
)
O(N^2)
O(N2)
Code:
#include<bits/stdc++.h>
using namespace std;
#define I inline
#define fi first
#define se second
#define pb push_back
#define ALL(X) (X).begin(), (X).end()
#define CLR(A, X) memset(A, X, sizeof(A))
#define bcnt(X) __builtin_popcountll(X)
typedef long long LL;
typedef pair<int, int> PII;
const int N = 3e3+10;
LL a[N], s[N], dp[N][N], f[N];
I void work() {
int n; scanf("%d", &n);
if(!n) exit(0);
for(int i = 1; i <= n; i++) {
scanf("%lld", a+i);
s[i] = s[i-1]+a[i];
f[i] = 1e18;
}
for(int r = 1; r <= n; r++) {
for(int l = r; l >= 1; l--) {
dp[l][r] = dp[l+1][r]+min(l*a[l]+s[r]-s[l], (3*r-2*l)*a[l]);
LL tmp = dp[l][r]-l*(s[r]-s[l-1])+(4*(r-l)+2)*(s[n]-s[r]);
f[r] = min(f[r], f[l-1]+tmp);
}
}
printf("%lld\n", f[n]);
}
int main() {
if(fopen("ebola.in", "r")) {
freopen("ebola.in", "r", stdin);
freopen("ebola.out", "w", stdout);
}
for(;;) work();
return 0;
}