题目描述
难度分:1700
输入n(1≤n≤2×105),m(1≤m≤109)和长为n的数组a(1≤a[i]≤m)。
硬盘可用空间为m。
有n个视频,下载第i个需要a[i]分钟,占用a[i]的硬盘空间。
- 同一时间只能下载一个视频,一旦视频开始下载,就会立刻占用a[i]的硬盘空间。
- 视频下完才能看。每个视频都恰好花1分钟看完,看完后可以立刻删除视频,释放空间。
- 你可以在看视频的同时下载另一个视频(如果硬盘空间足够的话)。
你需要下载并看完这n个视频。问:到看完最后一个视频,最少要用多少分钟?
输入样例1
5 6
1 2 3 4 5
输出样例1
16
输入样例2
5 5
1 2 3 4 5
输出样例2
17
输入样例3
4 3
1 3 2 3
输出样例3
12
算法
贪心构造+双指针
很显然下载时间肯定是满打满算的,如果磁盘空间只能存放一个视频,那答案肯定就是n+Σni=1a[i]。否则就可以边看边下载,从而节省时间。
如果存在两个视频i和j满足a[i]+a[j]≤m,那就可以在下载其中一个的同时观看另外一个,节省1的时间(观看一个视频的时间,此时另一个视频也下载了1的时间)。所以目的就是尽可能多凑出一些a[i]+a[j]≤m的数对(i,j),并且在这个过程中保证磁盘中最多只有两个视频。先对a排序,假设选出来k个数对,则可以这样构造k+1、1、k、2、……,这样构造可以让相邻两个视频的占用空间之和的最大值最小化。因此可以用相对而行的双指针来进行配对,在原始答案n+Σni=1a[i]的基础上不断减小1单位的时间代价得到最终答案。
复杂度分析
时间复杂度
对a排序的时间复杂度是O(nlog2n),双指针算法的时间复杂度是O(n),因此整体的时间复杂度为O(nlog2n)。
空间复杂度
双指针算法只使用了有限几个变量,因此额外空间的瓶颈在于排序,按照快排来算应该为O(log2n)。
C++ 代码
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 200010;
int n, m, a[N];
int main() {
scanf("%d%d", &n, &m);
LL ans = n;
for(int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
ans += a[i];
}
sort(a + 1, a + n + 1);
int l = 1, r = n;
while(l < r) {
if(a[l] + a[r] <= m) {
++l;
ans -= l < r? 2: 1;
}
--r;
}
printf("%lld\n", ans);
return 0;
}