头歌实训:循环调度法
文章目录
任务描述
本关任务:编写一个模拟CPU处理任务的循环调度法的程序。
本关要求您设计一个队列或者使用C++ STL的队列容器(queue)完成给定任务(见后面的编程要求)。
相关知识
为了完成本关任务,你需要掌握:1.如何设计和实现一个队列,2.如何使用C++ STL的队列容器queue。
1. 如何设计一个队列
略。
2. C++ STL的队列容器queue
queue翻译为队列,在STL中主要用来实现一个先进先出(First In First Out,FIFO)的容器。
如果要使用queue, 需要添加queue头文件,#include 。除此之外,还需要在头文件下面加上一句:“using namespace std;”,这样就可以在代码中使用queue 了。下面来看queue 的一些常用用法。
2.1 queue的定义
定义一个queue的语法如下,其中T可以是任意基本数据类型或容器:
queue queue_name;
2.2 queue容器内元素的访问
由于队列(queue)本身即是一种先进先出的限制性的数据结构,因此在STL中只能通过front()来访问队首元素,通过back()来访问队尾元素。
示例如下:
#include <stdio.h>
#include
using namespace std;
int main() {
queue q;
for (int i = 1; i <= 5; i++) {
q.push(i); //push(i)用来将i入队列,即依次入队列1 2 3 4 5
}
printf(“%d %d\n”, q.front(), q.back());//输出结果是1 5
return 0;
}
输出结果:
1 5
2.3 queue常用函数实例解析
(1) push(): push(x)将x入队列,时间复杂度为O(1)。
(2) front()、back(): front()和back()分别获得队首元素和队尾元素,时间复杂度O(1)。
(3) pop(): pop()令队首元素出队列,时间复杂度为O(1)。
示例如下:
#include <stdio.h>
#include
using namespace std;
int main() {
queue q;
for (int i = 1; i <= 5; i++) {
q.push(i); //push(i)用来将i入队列,即依次入队列1 2 3 4 5
}
for (int i = 1; i <= 3; i++) {
q.pop(); //出队首元素三次(即依次出队1 2 3)
}
printf(“%d\n”, q.front()); //此时队首元素是4,因此输出结果是4
return 0;
}
输出结果:
4
(4) empty() empty()检测queue是否为空,为空则返回true,非空则返回false。复杂度为O(1)。
示例如下:
#include <stdio.h>
#include
using namespace std;
int main() {
queue q;
if (q.empty() == true) { //一开始队列内没有元素,所以是空
printf(“Empty\n”);
} else {
printf(“Not empty\n”);
}
q.push(1); //1入队列
if (q.empty() == true) { //在入队列“1”后,队列非空
printf(“Empty\n”);
} else {
printf(“Not empty\n”);
}
return 0;
}
输出结果:
Empty
Not empty
(5) size(): size()返回queue内元素的个数,复杂度为O(1)。
示例如下:
#include <stdio.h>
#include
using namespace std;
int main() {
queue q;
for (int i = 1; i <= 5; i++) {
q.push(i); //push(i)用来将i入队列
}
printf(“%d\n”, q.size()); //队列中有5个元素
return 0;
}
输出结果:
5
2.4 queue的注意事项
当需要实现广度优先搜索(BFS)时,可以不用自己动手实现一个队列,而是用queue作为代替,以提高程序的准确性。
另外有一点需要注意的是,使用front()和pop()函数前,必须使用empty()函数先判断队列是否为空,否则可能因为队列空而出现错误。
STL的容器中还有两种容器跟队列有关,分别是双端队列(deque)和优先队列(priority_queue),前者是首尾两端都可以插入和删除的队列,后者是使用堆实现的默认将当前队列最大元素置于队首的容器(相当于大顶堆、大根堆)。在求解哈夫曼树和哈夫曼编码时就可以使用优先队列(priority_queue)。
2.5 一个完整的队列程序示例
#include
#include
using namespace std;
int main()
{
queue q; //定义队列对象q
for (int i = 1; i <= 10; i++) {
q.push(i); //1 2 3 4 5 6 7 8 9 10依次入队列
}
//输出队列的元素个数
cout << q.size() << endl; //10
//输出队首元素
cout << q.front() << endl; //1
//输出队尾元素
cout << q.back() << endl; //10
//输出队列的所有元素
while (!q.empty()) {
int tmp = q.front(); //取队首元素
cout << tmp << " "; //输出队首元素
q.pop(); //删除队首元素
}
cout << endl;
return 0;
}
输出结果:
10
1
10
1 2 3 4 5 6 7 8 9 10
编程要求
现有名称为namei
且处理时间为timei
的n个任务按顺序排成一列,CPU通过循环调度法逐一处理这些任务,每个任务最多处理q ms (这个时间称为时间片)。如果q ms 之后任务尚未处理完毕,那么该任务将被移动至队列最末尾,CPU随即开始处理下一个任务。
举个例子,假设q是100,然后有如下任务队列。
A(150) - B(80) - C(200) - D(200)
首先A被处理100 ms,然后带着剩余的50 ms移动至队尾。
B(80) - C(200) - D(200) - A(50)
随后B被处理80 ms,在总计第180 ms时完成处理,从队列中消失。
C(200) - D(200) - A(50)
接下来C被处理100 ms,然后带着剩余的100 ms移动至队尾。
D(200) - A(50) - C(100)
之后同理,一直循环到处理完所有任务。
请编写一个程序,模拟CPU循环调度法。
输入:
输入形式如下。
n q
name1 time1
name2 time2
…
namen timen
第1行输入表示任务数的整数n与表示时间片的整数q,用1个空格隔开。
接下来n行输入各任务的信息。字符串namei
与timei用1个空格隔开。
输出:
按照任务完成的先后顺序输出各任务名以及结束时间,任务名与对应结束时间用空格隔开,每一对任务名与结束时间占1行。
限制:
1 ≤ n ≤ 100000
1 ≤ q ≤ 1000
1 ≤ timei≤ 50000
1 ≤ namei的长度 ≤ 10
1 ≤ timei总和 ≤ 1000000
输入示例:
5 100
p1 150
p2 80
p3 200
p4 350
p5 20
输出示例:
p2 180
p5 400
p1 450
p3 550
p4 800
开始你的任务吧,祝你成功!
源代码:
#include <bits/stdc++.h>
using namespace std;
typedef struct { //定义一个结构task,其中tn用于存储任务名,为一个字符数组,t用于完成该单个任务所需时间t
char tn[10];
int t;
}task;
//队列最大容量
#define maxSize 100005
int main()
{
int n, q; //任务个数n和时间片q
queue<task> qtask, qtask2; //获得队列qtask,其中队列元素为结构task类型,队列qtask2用来存储完成的任务以及所花费的时间;
scanf("%d%d", &n, &q); //输入任务个数和时间片p
for(int i = 1; i <= n; i++) //for循环输入任务
{
task ts; //获得结构ts
scanf("%s%d", ts.tn, &ts.t); //输入任务名称和处理任务所需时间,数组名本身就是地址因此不要用取地址符号&
qtask.push(ts); //将ts入任务队列qtask
}
int count = 0; //计数器count记录任务处理所需总时间
while(!qtask.empty()) //当任务队列qtask还没处理完时遍历
{
task p, j = qtask.front(); //结构p用来存入完成队列qtask2中,结构j用来获取qtask的队首元素
if(j.t <= q) //当队首元素处理任务所需时间比时间片p小时
{
count += j.t; //总花费时间加上处理了的任务时间
p.t = count; //完成任务的队列存储总花费时间
strcpy(p.tn, j.tn); //将任务队列中任务的名称复制到完成任务队列中去,用了strcpy函数j.tn->p.tn
qtask2.push(p); //将p入队到完成任务队列qtask2中去
qtask.pop(); //将任务队列中完成的任务弹出
}
else //当完成任务所需时间大于时间片p时
{
count += q; //完成任务总时间加上时间片
j.t = j.t - q; //任务队列中完成任务所需时间减去时间片
qtask.pop(); //将任务队列队首元素弹出
qtask.push(j); //将未完成的任务入队到任务队列队尾
}
}
while(!qtask2.empty()) //遍历完成任务队列,根据其先进先出的性质将元素输出
{
task p2 = qtask2.front(); //获得完成任务队列的队首元素
printf("%s %d\n", p2.tn, p2.t); //打印名称和所用时间
qtask2.pop(); //将队首元素弹出
}
return 0;
}

353

被折叠的 条评论
为什么被折叠?



