在
x
x
x轴上有
n
n
n个点(从
1
1
1到
n
n
n),每个点都有一个值
0
0
0或
1
1
1,
0
0
0代表这个点不能走,
1
1
1代表这个点可以走。你可以对这
n
n
n个点执行一下两个操作:
1. 花费
x
x
x让一个值为
0
0
0的点变为
1
1
1。
2. 花费
y
y
y删除第一个点,其余的点的
x
x
x轴坐标整体减一。
完成上面操作之后,你从点
p
p
p出发,每次向前跳
k
k
k个单位,即你能跳的位置有
{
p
,
p
+
k
,
p
+
2
k
,
.
.
.
}
\{p,p+k,p+2k,...\}
{p,p+k,p+2k,...} 直到跳出这
n
n
n个点。若在跳出这
n
n
n个点之前落在了一个值为
0
0
0的点上,那么就失败了。
那么为了能成功的跳出这
n
n
n个点,最小的花费是多少?
思路:
一般的思路,枚举前面一共移除多少块,然后从
p
p
p点开始,每次往前跳
k
k
k个单位,遇到
0
0
0就在总花费中加上
x
x
x,最终取花费最小的作为答案,但是复杂度高达
O
(
n
2
/
k
)
O(n^2/k)
O(n2/k)这是不能接受的。
我们可以先对这些数据进行预处理,利用DP从后往前算出花费,
d
p
[
i
]
dp[i]
dp[i]表示的状态是:当
i
i
i点作为
p
p
p点,要跳出
n
n
n个点的花费(这里不包括移除前面的点的花费),转移方程为
f
(
x
)
=
{
d
p
[
i
]
=
a
[
i
]
=
=
1
?
0
:
x
,
i
+
k
>
n
d
p
[
i
]
=
d
p
[
i
+
k
]
+
a
[
i
]
=
=
1
?
0
:
x
,
i
+
k
<
=
n
f(x)=\left\{\begin{aligned}&dp[i]=a[i]==1?0:x,&i+k>n\\&dp[i]=dp[i+k]+a[i]==1?0:x,&i+k<=n\end{aligned}\right.
f(x)={dp[i]=a[i]==1?0:x,dp[i]=dp[i+k]+a[i]==1?0:x,i+k>ni+k<=n
处理完这些数据就可以从
p
p
p点开始枚举每一个点,那么从这点出发的总花费就是
(
i
−
p
)
∗
y
+
d
p
[
i
]
(i-p)*y+dp[i]
(i−p)∗y+dp[i],全部枚举完取最小的一个就是答案。
AC代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
typedef long long ll;
const int Maxn = 100005;
const int INF = 0x3f3f3f3f;
int dp[Maxn], a[Maxn];
void solve() {
int n, p, k;
scanf("%d %d %d", &n, &p, &k);
for (int i = 1; i <= n; i++) {
scanf("%1d", a + i);
}
int x, y;
scanf("%d %d", &x, &y);
for (int i = n; i >= p; i--) {
if (i + k > n) {
dp[i] = a[i] == 1 ? 0 : x;
} else {
dp[i] = dp[i + k] + (a[i] == 1 ? 0 : x);
}
}
int ans = INF;
for (int i = p; i <= n; i++) {
ans = std::min(ans, (i - p) * y + dp[i]);
}
printf("%d\n", ans);
}
int main() {
int T;
scanf("%d", &T);
while (T--) {
solve();
}
return 0;
}