P5017 摆渡车
知识点:线性DP,DP优化
我懂个卵子的DP。
原题面
算法一
设 \(f_i\) 在 \(i\) 时间发车,发车时等待的时间和的最小值。则显然有:
对于每个 \(f_i\),当在 \(i\) 时刻时发第一班车,\(f_i\)最大,则其初始值为:
为保证载上所有人,最后一班车需在 \([t_{max}, t_{max}+m)\)内发车,则:
复杂度 \(O(nt^2)\),期望得分 \(30\text{pts}\)。
算法二
前缀和优化。
设 :
对于上式中的 \(\sum\limits_{j < t_k \le i}{i-t_k}\),有:
替换状态转移方程。
复杂度 \(O(t^2)\),期望得分 \(50\text{pts}\)。
算法三
优化转移方程。
对于状态转移方程:
显然,若 \(j\le i-2m\),则在 \(j+m\) 时刻可多发一班车,不影响在 \(i\) 时刻发车,且答案不会变劣。
即:停车时间 \(i- (j+m) < m\)。
故转移方程可替换为:
复杂度 \(O(mt)\),期望得分 \(70\text{pts}\)。
算法四
减去无用状态。
若 \(cnt_i=cnt_{i-m}\),说明时间段 \([i-m,i]\)内没有需要坐车的人。
则在 \(i-m\) 时刻发车,在 \(i\) 时刻不发车,不会使答案变劣,\(f_i\) 是一个无用状态。
则可将状态转移方程改为:
当满足 \(t_i = t_{i-1}+m\) 时,有用的位置最多,为 \(nm\) 个。
复杂度 \(O(nm^2 + t)\),期望得分 \(100\text{pts}\)。
算法五
由算法三,停车时间 \(i- (j+m) < m\)。
车往返一次时间 \(m\),则人等车时间 \(< 2m\)。
设 \(f_{i,j}\) 表示第 \(i\) 个人,等待时间为 \(j\),且前 \(i\) 个人已到达的时间最小值。
初始值 \(f_{1,i} = i\)。
分类讨论:
- 若 \(t_{i+1} \le t_{i+1} + j\),则第 \(i+1\) 个人可和 第 \(i\) 个人坐同一辆车走。
第 \(i+1\) 个人的等待时间 \(k = t_i+j-t_{i+1}\)
状态转移方程式为:
- 否则,枚举第 \(i+1\) 个人的等待时间 \(k\)。\[k\in [\max(0, t_i + j + m - t_{i+1}), 2m) \]状态转移方程式同上,为:\[f_{i+1,k} = \min (f_{i+1}, k, f_{i,j} + k) \]
复杂度 \(O(nm^2)\),期望得分 \(100\text{pts}\)。
算法四
//知识点:DP
/*
By:Luckyblock
*/
#include <cstdio>
#include <ctype.h>
#include <cstring>
#include <algorithm>
#define ll long long
const int kMaxn = 4e6 + 2077;
//=============================================================
ll n, m, ans, maxt;
ll cnt[kMaxn], pos[kMaxn], f[kMaxn];
//=============================================================
inline ll read() {
int f = 1, w = 0; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1;
for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
return f * w;
}
//=============================================================
int main() {
n = read(), m = read();
for (int i = 1; i <= n; ++ i) {
ll t = read();
maxt = std :: max(maxt, t);
cnt[t] ++, pos[t] += t;
}
for (int i = 1; i < maxt + m; ++ i) {
cnt[i] += cnt[i - 1];
pos[i] += pos[i - 1];
}
memset(f, 63, sizeof(f)); ans = f[0];
for (int i = 0; i < maxt + m; ++ i) {
if (i >= m && cnt[i - m] == cnt[i]) {
f[i] = f[i - m];
continue;
}
f[i] = cnt[i] * i - pos[i];
for (int j = std :: max(i - 2 * m, 0ll); j <= i - m; ++ j) {
f[i] = std :: min(f[i],
f[j] + (cnt[i] - cnt[j]) * i - (pos[i] - pos[j]));
}
}
for (int i = maxt; i < maxt + m; ++ i) {
ans = std :: min(ans, f[i]);
}
printf("%lld\n", ans);
return 0;
}
算法五
空间需求比较奇怪,总是RE,于是开了很大。
//
/*
By:Luckyblock
*/
#include <cstdio>
#include <ctype.h>
#include <cstring>
#include <algorithm>
#define ll long long
const int kMaxn = 5010;
//=============================================================
ll n, m, ans, t[kMaxn], f[kMaxn][kMaxn / 4];
//=============================================================
inline ll read() {
int f = 1, w = 0; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1;
for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
return f * w;
}
//=============================================================
int main() {
n = read(), m = read();
for (int i = 1; i <= n; ++ i) t[i] = read();
std :: sort(t + 1, t + n + 1);
memset(f, 63, sizeof(f)); ans = f[0][0];
for (int i = 0; i <= 2 * m; ++ i) f[1][i] = i;
for (int i = 1; i <= n; ++ i) {
for (int j = 0; j < 2 * m; ++ j) {
if (t[i + 1] <= t[i] + j) {
int k = t[i] + j - t[i + 1];
f[i + 1][k] = std :: min(f[i + 1][k], f[i][j] + k);
} else {
for (int k = std :: max(0ll, t[i] + j + m - t[i + 1]); k < 2 * m; ++ k) {
f[i + 1][k] = std :: min(f[i + 1][k], f[i][j] + k);
}
}
}
}
for (int i = 0; i <= m; ++ i) ans = std :: min(ans, f[n][i]);
printf("%lld\n", ans);
return 0;
}