【头歌实训:循环调度法】

头歌实训:循环调度法

任务描述

本关任务:编写一个模拟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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值