ZOJ 1012 Mainframe
原题目链接
1012 主机
Ronald 先生在 ACM(数学计算代理)中负责主机的管理。代理会承接一些公司的数学计算任务,并在主机上完成任务后获得报酬。因此主机对 ACM 来说非常有价值。Ronald 先生需要安排主机上任务的运行顺序。当一个任务要运行时,他会检查任务的空闲资源,如果资源满足任务的要求,就把这些资源分配给任务,否则任务将暂停,直到有足够的资源。
因为刚开始不熟悉,所以他把所有的事情都搞乱了。久而久之,他便熟练了。并且,他总结了一套细则如下:
- 主机有M个CPU,可分配N个内存。
- 存在一个队列,用于存放等待执行的作业。您可以假设该队列足够大,可以容纳所有等待的作业。
- 一个作业 Ji 需要 Ai 个 CPU 和 Bi 个内存,在时间 Ti 到达队列。该作业需要在时间 Ui 之前完成。成功完成后,ACM 可获得 Vi( ) 作为奖励。如果提前完成,额外奖励为 W i ( ) 作为奖励。如果提前完成,额外奖励为 Wi( )作为奖励。如果提前完成,额外奖励为Wi()/小时。如果作业迟到,惩罚为 Xi( ) / 小时。例如,我们可以假设一个作业的价值为 10 )/小时。例如,我们可以假设一个作业的价值为 10 )/小时。例如,我们可以假设一个作业的价值为10,时间线为 8,惩罚为 2 / 小时。如果作业在时间 10 完成, A C M 将获得 10 − ( 10 − 8 ) ∗ 2 = 6 /小时。如果作业在时间 10 完成,ACM 将获得 10-(10-8)*2=6 /小时。如果作业在时间10完成,ACM将获得10−(10−8)∗2=6。
- 当作业开始执行时,所需的 CPU 和内存就被该作业占用,无法再分配给其他作业同时执行。作业完成后,这些资源将被释放。如果资源足够,则可以同时执行更多作业。
- 由于主机计算能力的共享,每个作业从开始执行到执行只需要一个小时就可以完成。你可以假设每个作业正好花费一个小时。
- 当没有作业需要执行时,主机将处于空闲状态,直到有作业到达作业队列。
- 如果有多个作业到达队列,则更有价值的作业将首先执行。你可以假设作业的价值总是不相等(ViVj)。
- 如果空闲的 CPU 或内存不能满足作业的需求,作业将暂停一小时,不占用任何资源。一小时后,将再次检查该作业的资源,而不考虑队列中的其他作业。如果需求再次不满足,作业将在接下来的一小时内保持暂停状态,队列中的其他作业将尝试分配资源。否则,作业将抢占所需的 CPU 和内存并开始执行。
- 当有多个作业被暂停时,先到达的作业将尝试首先被分配。
按照规则,Ronald先生可能把例行事务处理得很好。但现在,除了例行事务,ACM还要求他根据作业清单计算收益。给定时间线F,他必须计算已经执行或应该执行的作业。当然,根据作业Ji,如果Ui>F,作业没有执行,则不应计算在内;但那些已经执行或Ui<=F的作业应该计算在内。如果作业没有执行,它不会给ACM带来任何价值,这意味着只需要计算对时间线的惩罚。
确实,他的编程能力不够好,不喜欢手工计算。所以他对此感到不安。你能帮助他解决这个问题吗?
输入
输入包含若干个测试用例,每个用例描述了主机的资源和作业列表。每个测试用例以一行开头,其中包含一个整数 F,0 <= F <= 10000,时间行。下一行包含三个整数 M、N 和 L(M、N、L >= 0)。M 是主机中的 CPU 数量,N 是内存大小。L 表示作业列表中的作业数。最多会有 10000 个作业。
测试用例中接下来的L行描述了作业的信息。描述作业Ji的数据由7个整数Ai,Bi,Ti,Ui,Vi,Wi,Xi组成。Ai和Bi表示对CPU和内存的需求(Ai,Bi> = 0)。Ti和Ui表示作业到达的时间和时间表(0 <= Ti <= Ui)。Vi,Wi,Xi分别为作业的奖励,奖金和惩罚(Vi,Wi,Xi> = 0)。
输入文件以空测试用例(F=0)结尾。此用例不应被处理。
输出
你的程序必须根据作业列表计算主机的总收入。对于每个测试用例,打印用例编号、冒号和空格,然后打印收入。
每个测试用例后打印一个空白行。
注意:不计算尚未执行的作业,它们的时间线晚于 F。
示例输入
10
4 256 3
1 16 2 3 10 5 6
2 128 2 4 30 10 5
2 128 2 4 20 10 5
0
样本输入的输出
Case 1: 74
c++代码
#include<bits/stdc++.h>
using namespace std;
class work {
public:
int A;
int B;
int T;
int U;
int V;
int W;
int X;
bool select;
};
bool mycom(work a, work b) {
if (a.T != b.T) return a.T < b.T;
else return a.V > b.V;
}
int main() {
int F, M, N, L, cont = 0;
while (cin >> F) {
if (F == 0) break;
cont++;
cin >> M >> N >> L;
vector<work> works(L);
for (int i = 0; i < L; i++) {
cin >> works[i].A >> works[i].B >> works[i].T >> works[i].U >> works[i].V >> works[i].W >> works[i].X;
works[i].select = false;
}
sort(works.begin(), works.end(), mycom);
vector<work> current_work;
int sum = 0;
for (int i = 0; i <= F; i++) {
for (work w : current_work) {
if (i > w.U) sum += w.V - (i - w.U) * w.X;
else if (i < w.U) sum += w.V + (w.U - i) * w.W;
else sum += w.V;
M += w.A;
N += w.B;
}
current_work.clear();
if (i == F) break;
for (int j = 0; j < works.size(); j++) {
if (i < works[j].T) break;
if (works[j].select || works[j].A > M || works[j].B > N) continue;
M -= works[j].A;
N -= works[j].B;
current_work.push_back(works[j]);
works[j].select = true;
}
}
for (int j = 0; j < works.size(); j++) {
if (!works[j].select && works[j].U <= F) sum -= (F - works[j].U) * works[j].X;
}
cout << "Case " << cont << ": " << sum << endl << endl;
}
return 0;
}//by wqs
题目解析
这个题目并不是要你用贪心算法求出能获得的最大利润是多少,我一开始以为要我求这个,走了坑。
实际上题目意思是你按照题目给你的顺序来执行,最后的利润是多少。
我们不需要考虑怎样执行利润最大,只要按照顺序执行就行。
最早到达的最先执行,除非暂时执行不了(可能内存不够用)
同时到达的先执行价值大的。
如果最先到达的可以执行了(内存释放了),先执行最先到达的。
对于那些已经截止并且没有完成的工作,要计算罚金
解题步骤
给工作排序
最早到达的最先执行,除非暂时执行不了(可能内存不够用)
同时到达的先执行价值大的。
bool mycom(work a, work b) {
if (a.T != b.T) return a.T < b.T;
else return a.V > b.V;
}
sort(works.begin(), works.end(), mycom);
用一个数组存储正在执行的工作,1小时后结算
vector<work> current_work;
开始时间戳循环
对于时间戳0不需考虑结算执行的工作,因为还没有开始
对于时间戳F不需要考虑是否添加工作,因为已经结束。
否则既要结算执行的工作,又要引进新工作
for (int i = 0; i <= F; i++) {
for (work w : current_work) {//结算工作
if (i > w.U) sum += w.V - (i - w.U) * w.X;//惩罚
else if (i < w.U) sum += w.V + (w.U - i) * w.W;//奖励
else sum += w.V;
M += w.A;//释放内存
N += w.B;
}
current_work.clear();
if (i == F) break;
for (int j = 0; j < works.size(); j++) {//添加新工作
if (i < works[j].T) break;//工作时间还没到
if (works[j].select || works[j].A > M || works[j].B > N) continue;
M -= works[j].A;
N -= works[j].B;
current_work.push_back(works[j]);//添加工作
works[j].select = true;
}
}
结算
对于那些已经截止并且没有完成的工作,要计算罚金
for (int j = 0; j < works.size(); j++) {
if (!works[j].select && works[j].U <= F) sum -= (F - works[j].U) * works[j].X;
}