[NOIP2018 普及组] 摆渡车
题目描述
有 n n n 名同学要乘坐摆渡车从人大附中前往人民大学,第 i i i 位同学在第 t i t_i ti 分钟去 等车。只有一辆摆渡车在工作,但摆渡车容量可以视为无限大。摆渡车从人大附中出发、 把车上的同学送到人民大学、再回到人大附中(去接其他同学),这样往返一趟总共花费 m m m 分钟(同学上下车时间忽略不计)。摆渡车要将所有同学都送到人民大学。
凯凯很好奇,如果他能任意安排摆渡车出发的时间,那么这些同学的等车时间之和最小为多少呢?
注意:摆渡车回到人大附中后可以即刻出发。
输入格式
第一行包含两个正整数
n
,
m
n, m
n,m,以一个空格分开,分别代表等车人数和摆渡车往返一趟的时间。
第二行包含
n
n
n 个正整数,相邻两数之间以一个空格分隔,第
i
i
i 个非负整数
t
i
t_i
ti 代表第
i
i
i 个同学到达车站的时刻。
输出格式
输出一行,一个整数,表示所有同学等车时间之和的最小值(单位:分钟)。
样例 #1
样例输入 #1
5 1
3 4 4 3 5
样例输出 #1
0
样例 #2
样例输入 #2
5 5
11 13 1 5 5
样例输出 #2
4
提示
【输入输出样例 1 说明】
同学
1
1
1 和同学
4
4
4 在第
3
3
3 分钟开始等车,等待
0
0
0 分钟,在第
3
3
3 分钟乘坐摆渡车出发。摆渡车在第
4
4
4 分钟回到人大附中。
同学
2
2
2 和同学
3
3
3 在第
4
4
4 分钟开始等车,等待
0
0
0 分钟,在第
4
4
4 分钟乘坐摆渡车 出发。摆渡车在第
5
5
5 分钟回到人大附中。
同学
5
5
5 在第
5
5
5 分钟开始等车,等待
0
0
0 分钟,在第
5
5
5 分钟乘坐摆渡车出发。自此 所有同学都被送到人民大学。总等待时间为
0
0
0。
【输入输出样例 2 说明】
同学
3
3
3 在第
1
1
1 分钟开始等车,等待
0
0
0 分钟,在第
1
1
1 分钟乘坐摆渡车出发。摆渡 车在第
6
6
6 分钟回到人大附中。
同学
4
4
4 和同学
5
5
5 在第
5
5
5 分钟开始等车,等待
1
1
1 分钟,在第
6
6
6 分钟乘坐摆渡车 出发。摆渡车在第
11
11
11 分钟回到人大附中。
同学
1
1
1 在第
11
11
11 分钟开始等车,等待
2
2
2 分钟;同学
2
2
2 在第
13
13
13 分钟开始等车, 等待
0
0
0 分钟。他/她们在第
13
13
13 分钟乘坐摆渡车出发。自此所有同学都被送到人民大学。 总等待时间为
4
4
4。
可以证明,没有总等待时间小于
4
4
4 的方案。
【数据规模与约定】
对于
10
%
10\%
10% 的数据,
n
≤
10
,
m
=
1
,
0
≤
t
i
≤
100
n ≤ 10, m = 1, 0 ≤ t_i ≤ 100
n≤10,m=1,0≤ti≤100。
对于
30
%
30\%
30% 的数据,
n
≤
20
,
m
≤
2
,
0
≤
t
i
≤
100
n ≤ 20, m ≤ 2, 0 ≤ t_i ≤ 100
n≤20,m≤2,0≤ti≤100。
对于
50
%
50\%
50% 的数据,
n
≤
500
,
m
≤
100
,
0
≤
t
i
≤
1
0
4
n ≤ 500, m ≤ 100, 0 ≤ t_i ≤ 10^4
n≤500,m≤100,0≤ti≤104。
另有
20
%
20\%
20% 的数据,
n
≤
500
,
m
≤
10
,
0
≤
t
i
≤
4
×
1
0
6
n ≤ 500, m ≤ 10, 0 ≤ t_i ≤ 4 \times 10^6
n≤500,m≤10,0≤ti≤4×106。
对于
100
%
100\%
100% 的数据,
n
≤
500
,
m
≤
100
,
0
≤
t
i
≤
4
×
1
0
6
n ≤ 500, m ≤ 100, 0 ≤ t_i ≤ 4 \times 10^6
n≤500,m≤100,0≤ti≤4×106。
思路
先研究每个学生需要等待的最长的时间。
- 假设在t时刻,车刚出发,而学生在t+1时刻到达,此时他需要等待车回来。等车时间T1<m
- 车回来后,此时为t+m,车最多等待m秒。如果更长,它完全可以先开一次,再回来。车从回来,到重新出发时间T2<m
综上,学生需要等待的最多时间为T1+T2<2m秒。
其次,应该按照学生的抵达顺序来先后处理,排个序总没错。
sort(a, a + n);
每个学生的抵达时间已知,以及他们对应的最长等待时间已知。再根据题目数据范围。
很容易想到二维dp
定义dp[i][j]表示,第i个学生,在等待j时刻后重新上车。此时,运送前i个学生,需要花费的最小等待时间。
暴力dp
来个暴力dp
用dp[i-1][k]来更新dp[i][j],总共就两种情况:
- 刚好可以蹭到上一班的车。a[i-1] + k == a[i] + j
- 等待了j时刻后,上一班的车已经重新回来了。a[i-1] + k + m <= a[i] + j
时间复杂度O(4nmm)。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define pcc pair<char, char>
#define inf 0x3f3f3f3f
const int maxn = 510;
const int maxm = 210;
int n, m, m2;
int a[maxn];
int dp[maxn][maxm];
void solve() {
scanf("%d%d", &n, &m);
for (int i = 0; i < n; ++i) {
scanf("%d", &a[i]);
}
sort(a, a + n);
m2 = m * 2;
for (int j = 0; j < m2; ++j) {
dp[0][j] = j;
}
for (int i = 1; i < n; ++i) {
int tmp = a[i] - a[i-1];
for (int j = 0; j < m2; ++j) {
dp[i][j] = inf;
for (int k = 0; k < m2; ++k) {
// case1: a[i-1] + k == a[i] + j.
// case2: a[i-1] + k + m <= a[i] + j
if (k == tmp + j || k + m <= tmp + j) {
if (dp[i-1][k] != inf) dp[i][j] = min(dp[i][j], dp[i-1][k] + j);
}
}
}
}
int res = inf;
for (int j = 0; j < m2; ++j) {
res = min(dp[n-1][j], res);
}
printf("%d\n", res);
}
int main() {
int t;
// scanf("%d", &t);
t = 1;
while (t--) {
solve();
}
}
/*
*/
前缀和优化
根据上述dp转移方程,我们容易发现可以前缀和优化下。
优化后时间复杂度O(2nm)
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define pcc pair<char, char>
#define inf 0x3f3f3f3f
const int maxn = 510;
const int maxm = 210;
int n, m, m2;
int a[maxn];
int dp[maxn][maxm];
int pre[maxm], cur[maxm];
void solve() {
scanf("%d%d", &n, &m);
for (int i = 0; i < n; ++i) {
scanf("%d", &a[i]);
}
sort(a, a + n);
m2 = m * 2;
for (int j = 0; j < m2; ++j) {
dp[0][j] = j;
pre[j] = 0;
}
for (int i = 1; i < n; ++i) {
int tmp = a[i] - a[i-1];
for (int j = 0; j < m2; ++j) {
dp[i][j] = inf;
if (tmp + j - m >= 0 && pre[min(tmp+j-m, m2-1)] != inf) {
dp[i][j] = min(dp[i][j], pre[min(tmp+j-m, m2-1)] + j);
}
if (tmp + j >= 0 && tmp + j < m2 && dp[i-1][tmp+j] != inf) {
dp[i][j] = min(dp[i][j], dp[i-1][tmp+j] + j);
}
if (j) {
cur[j] = min(cur[j-1], dp[i][j]);
} else {
cur[j] = dp[i][j];
}
/*
for (int k = 0; k < m2; ++k) {
// case1: a[i-1] + k == a[i] + j.
// case2: a[i-1] + k + m <= a[i] + j
if (k == tmp + j || k + m <= tmp + j) {
if (dp[i-1][k] != inf) dp[i][j] = min(dp[i][j], dp[i-1][k] + j);
}
}*/
}
for (int j = 0; j < m2; ++j) {
pre[j] = cur[j];
}
}
int res = inf;
for (int j = 0; j < m2; ++j) {
res = min(dp[n-1][j], res);
}
printf("%d\n", res);
}
int main() {
int t;
// scanf("%d", &t);
t = 1;
while (t--) {
solve();
}
}
/*
*/
瞄了眼洛谷上别人的dp题解,好像整得挺复杂的,还是自己的简洁233(自恋ing)