题目来源:
网易云课堂 算法设计与分析之入门篇 贪心算法 作业3
题目内容:
有 n 个人,要完成 2 门课程。其中第 i 个人学习一门课程所需要的时间是 ti。
每个人同一时刻只能修一门课程,每门课程同一时刻只能被一个人修,中间不能中断。
问所有人都修完 2 门课程,至少需要多少时间。
输入格式:
输入的第一行包含一个整数 n(1 <= n <= 1000000)。
接下来的一行包含 n 个整数 (1 <= ai <= 1000000000),表示每门课程的时间。
输出格式:
输出一行表示对应的答案。
输入样例:
3
2 2 2
3
4 1 2
4
1 3 2 1
输出样例:
6
8
7
时间限制:2000ms内存限制:128000kb
题目分析:
理解一下题意,说的是把两个{ai}1<=i<=n中的元素分别排好,并且不能重叠。
这个问题难在如何找到一个好的策略来调度,只要找到这样一个调度策略,证明它是总时间最少的策略就可以了,代码部分并不难。
为讨论方便,我们假设a1<=...<=ai<=...<=an
显然,我们最希望看到的解是所有ai的和。在这种情况下,所有的课程都没有重叠。我们可以看看重叠的情况,亦即样例2:
4444221_
221_4444
由于重叠,第二行的课程4必须往后挪一位,导致总时间增加。为减少重叠,我采用了以下的策略:
an a(n-1) a(n-2) ... a2 a1
a(n-1) a(n-2) ... a1 an
假设从0开始计数,所以上面一行ai(i<n) 的开始时间为an+...+a(i+1) ,下面一行ai 的结束时间为
a(n-1)+...+ai ,由于ai<=a(i+1) (i<n) ,所以上面一行ai开始时间总大于下面一行ai。
通过循环移动一位,我发现整个策略里唯一可能重叠的课程就是最大的an了。当 2 * an <= an +...+a1 时,an不重叠,所有的课程都不重叠,最小的总时间为所有课程时间之和;否则,an重叠,最小的总时间为2 * an 。
回到实现部分,我们只需要遍历一下数组,找到最大的元素,同时求出最大值,比较最大值的两倍和所有元素之和就可以了。同时注意一下给的数有可能会溢出,选大一点的long long就行了。
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
typedef unsigned int uint_32;
typedef unsigned long long uint_64;
uint_64 GetMinCourseTime(vector<uint_32>& courses) {
if ( courses.size() == 0 ) return 0;
uint_64 sum = 0;
uint_64 maxnum = courses[0];
for (uint_32 i = 0; i < courses.size(); i++) {
if (maxnum < courses[i])maxnum = courses[i];
sum += courses[i];
}
return 2*maxnum>sum?2*maxnum:sum;
}
int main() {
uint_32 n = 0;
cin >> n;
vector<uint_32> scores;
for (uint_32 i = 0; i < n; i++) {
uint_32 ele = 0;
cin >> ele;
scores.push_back(ele);
}
cout << GetMinCourseTime(scores) << "\r\n";
system("pause");
return 0;
}