题目
有个
n
\,n\,
n路标,长
l
\,l\,
l的路,选其中至多
k
\,k\,
k个路标,使得走完全程所用时间最少。
(
n
<
500
)
(n<500)
(n<500)
给出每个路标的位置和限速,到下一个路标前必须以这个速度走,第一个路标不可移除。
思考
显然是个
n
3
n^3
n3 DP,考虑
d
p
[
i
]
[
j
]
dp[\,i\,][\,j\,]
dp[i][j] 表示前
j
−
1
j\!-\!1
j−1 个路标至多去掉
i
i
i 个走到
j
j
j 的最小费用。
为什么不能去掉
j
j
j 点呢,因为DP要求前面状态不能对后面有影响,所以每个状态必须是确定的,
j
j
j 一定不选,后面从前面状态转移过来就有明确的限速了。
那又为什么说至多去掉
i
i
i 个呢,去掉
i
i
i 个不更明确吗,其实DP的重点在于每个DP数组保存的都是当前状态最优情况,重点在于最优,因为最优的时候可能选不够
i
i
i 个,转移的时候也是从最优的情况转移过去。
考虑一下有啥思路,先想想为什么不能贪心。因为如果贪心,每次选贡献最大的,一种情况,去掉前两个贡献小的,后面一个贡献就会非常大的情况就会错,那么我们容易想出来一个结论,就是每次选的一定是一整段或者一个,这就是从 j − 1 j\!-\!1 j−1 个数中选出若干段组成 i i i 个数。
先写出状态转移方程
d
p
[
i
]
[
j
]
=
m
i
n
(
d
p
[
i
]
[
j
]
,
d
p
[
i
−
p
]
[
j
−
p
−
1
]
+
c
o
s
t
)
dp[\,i\,][\,j\,] = min( dp[\,i\,][\,j\,],\ dp[\,i\!-\!p\,][\,j\!-\!p\!-\!1\,] + cost\ )
dp[i][j]=min(dp[i][j], dp[i−p][j−p−1]+cost )
p p p 是枚举的从 j − 1 j-1 j−1 位置开始往前数多少个数不选,别看虽然只是枚举了最后这一段,但是已经包含了所有的情况了,因为 d p [ i − p ] [ j − p − 1 ] dp[\,i\!-\!p\,][\,j\!-\!p\!-\!1\,] dp[i−p][j−p−1] 包含了前面一段选各种段或者不选的最优解。
注意一下,要记得在终点增加一个点,不能被删,也用来计算答案。
代码
#include <bits/stdc++.h>
using namespace std;
#pragma GCC optimize (Ofast)
#define fastio ios_base::sync_with_stdio(0); cin.tie(NULL);
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define per(i, a, b) for(int i = (a); i >= (b); i--)
#define LL long long
#define mp make_pair
#define pb push_back
#define fr first
#define se second
#define endl "\n"
#define debug1 cout << "???" << endl;
#define debug2(x) cout << #x << ": " << x << endl;
const int INF = 0x3f3f3f3f;
const int N = 5e2+7;
int n, l, k, dp[N][N];
pair <int, int> a[N];
int main()
{
fastio
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
memset(dp, INF, sizeof(dp));
cin >> n >> l >> k;
rep(i, 1, n)
cin >> a[i].fr;
rep(i, 1, n)
cin >> a[i].se;
a[n+1] = mp(l, 0);
dp[0][1] = 0;
rep(i, 2, n+1)
dp[0][i] = dp[0][i-1] + (a[i].fr - a[i-1].fr) * a[i-1].se;
rep(i, 1, k)
{
rep(j, 2, n+1)
{
if(i > j-1)
continue;
dp[i][j] = dp[i-1][j];
rep(p, 0, min(j-1, i))
{
int l = j-p-1;
dp[i][j] = min(dp[i][j], dp[i-p][l] + a[l].se * (a[j].fr - a[l].fr));
}
}
}
cout << dp[k][n+1] << endl;
return 0;
}