优先队列的拓扑排序算法笔试题

拼多多20届学霸批算法笔试题第三题

一共有N个执行的任务,每个任务需要Pi的时间完成执行。同时,任务之间可能会有一些依赖关系。比如任务1可能依赖任务2和任务3,那么任务1必须在任务2和任务3都执行完成后才能执行。

同时只能执行一个任务,并且在任务完成之前不能暂停切换去执行其他任务。为了提升平台用户的使用体验,希望最小化任务的平均返回时长。一个任务的返回时长定义为任务执行完成时刻减去平台接收到该任务的时刻。在零时刻,所有N个任务都已经被平台接收。

安排一下任务执行顺序,使得平均返回时长最小。

输入描述:

第一行包含2个正整数N、M,分别表示任务数量以及M个任务依赖关系。

第二行包含N个正整数,第i(1 <= i <= N)个数表示第i个任务的处理时间Pi。

接下来的M行,每行表示一个任务依赖关系。每行包含2个整数Ai(1 <= Ai <= N)、Bi(1 <= Bi <= N)。表示第Bi个任务依赖于第Ai个任务。数据保证不会出现循环依赖的情况。

数据范围:

 1 <= N <= 1000

 1 <= M <= 50000

 1 <= Pi <= 10000

输出描述:

输出一行,包含N个整数(两两之间用一个空格符分隔),其中第i(1 <= i <= N)个整数表示多多鸡执行的第i个任务的编号。若有多种可行方案,则输出字典序最小(优先执行编号较小的任务)的方案。

示例:

输入:

5 6

1 2 1 1 1

1 2

1 3

1 4

2 5

3 5

4 5

输出:

1 3 4 2 5

思路:

如果不考虑依赖关系,为了实现最小平均返回时长,需要按照耗时越少任务越早执行的顺序执行,加上依赖关系后,我们一定要做完依赖任务才能进行依赖后的任务,只有依赖任务的数量为0(表示依赖任务做完,才能进行本任务的执行),这里的执行顺序还是按照处理时间排序;使用小顶堆存储当前可以执行的任务, 每次出堆后更新依赖次数, 依赖次数为0的任务加入到优先队列中

C++代码:

#include<iostream>
#include<string>
#include <vector>
#include <list>
#include <queue>
#include<algorithm>


using namespace std;

typedef struct Task{
	int seq;    //任务序列号(1,2,3,...,N)
	int time;   //任务处理时间
	Task() :seq(0), time(0){}
}Task;

struct cmp{
	bool operator()(const Task &a, const Task &b)
	{
		return a.time != b.time ? a.time > b.time : a.seq > b.seq;  //输出字典序最小(优先执行编号较小的任务)
		//return a.time > b.time;   //将time利用小顶堆排序,首先将小数输出
		//return a.time < b.time    //将time利用大顶堆排序,首先将大数输出
	}
};

int main(void){
	int N, M;
	cin >> N >> M;    //输入的任务数量N(1,2,3,...,N)和任务依赖关系数量
	
	vector<Task> task(N + 1);   //存放任务
	vector<list<int> > Adj(N+1, list<int>());  //邻接表
	vector<int> inDegree(N+1, 0);     //保存每个节点的入度
	
	//输入任务的处理时间
	for (int i = 1; i < N + 1; ++i)
	{
		Task tmp;
		tmp.seq = i;
		cin >> tmp.time;
		task[i] = tmp;
	}
	//输入任务的依赖关系建立邻接矩阵
	for (int i = 0; i < M; ++i)
	{
		int u, v;
		cin >> u >> v;
		Adj[u].push_back(v);
		inDegree[v]++;
	}
	//将入度为0的任务放入队列
	priority_queue<Task, vector<Task>, cmp> taskque;
	for (int i = 1; i < N + 1; ++i)
	{
		if (inDegree[i] == 0)
			taskque.push(task[i]);
	}
	
	//进行拓扑排序
	vector<int> tasksort;
	while (!taskque.empty())
	{
		Task complete = taskque.top();
		taskque.pop();
		list<int>::iterator ite = Adj[complete.seq].begin();
		for (; ite != Adj[complete.seq].end(); ++ite)
		{
			inDegree[*ite]--;
			if (inDegree[*ite] == 0)
				taskque.push(task[*ite]);
		}
		tasksort.push_back(complete.seq);
	}
	//输出任务顺序
	for (auto task : tasksort)
	{
		cout << task << " ";
	}
	cout << endl;
	return 0;
}

--------------------------------------------------

参考博客:

C++ priority_queue用法(大顶堆,小顶堆)

【c++】STL里的priority_queue用法总结

拼多多2019学霸批笔试

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C语言中队列拓扑排序算法的设计可以分为以下几个步骤: 1. 定义一个结构体来存储图中的每个顶点,包括顶点的编号、入度数和邻接表等信息。 ``` typedef struct vertex { int id; // 顶点编号 int in_degree; // 顶点入度 struct edge* next; // 邻接表 } Vertex; ``` 2. 构建图并初始化每个顶点的入度数和邻接表。 ``` Vertex graph[N]; // N 为顶点数 // 初始化每个顶点的入度数和邻接表 for (int i = 0; i < N; i++) { graph[i].id = i; graph[i].in_degree = 0; graph[i].next = NULL; } // 构建图 for (int i = 0; i < M; i++) { // M 为边数 int from, to; // from -> to scanf("%d%d", &from, &to); // 添加一条从 from 到 to 的边 struct edge* new_edge = (struct edge*)malloc(sizeof(struct edge)); new_edge->to = to; new_edge->next = graph[from].next; graph[from].next = new_edge; graph[to].in_degree++; // 更新 to 的入度数 } ``` 3. 将所有入度为 0 的顶点入队。 ``` queue<int> q; for (int i = 0; i < N; i++) { if (graph[i].in_degree == 0) { q.push(i); } } ``` 4. 依次从队列中取出顶点并输出,同时更新与该顶点相邻的顶点的入度数。 ``` while (!q.empty()) { int cur = q.front(); q.pop(); printf("%d ", cur); // 输出顶点编号 // 更新与该顶点相邻的顶点的入度数 struct edge* p = graph[cur].next; while (p != NULL) { int next = p->to; graph[next].in_degree--; if (graph[next].in_degree == 0) { q.push(next); } p = p->next; } } ``` 5. 如果最终输出的顶点数量少于总顶点数,则说明存在环,拓扑排序失败。 完整代码如下: ``` #include <stdio.h> #include <stdlib.h> #include <queue> using namespace std; const int N = 10010; // 邻接表 typedef struct edge { int to; // 目标顶点编号 struct edge* next; // 下一条边 } Edge; // 图中每个顶点的信息 typedef struct vertex { int id; // 顶点编号 int in_degree; // 顶点入度 Edge* next; // 邻接表 } Vertex; Vertex graph[N]; // 图 int n, m; // 顶点数和边数 // 拓扑排序 bool topological_sort() { queue<int> q; // 将所有入度为 0 的顶点入队 for (int i = 1; i <= n; i++) { if (graph[i].in_degree == 0) { q.push(i); } } // 依次从队列中取出顶点并输出,同时更新与该顶点相邻的顶点的入度数 int cnt = 0; // 记录已经输出的顶点数 while (!q.empty()) { int cur = q.front(); q.pop(); cnt++; // 输出顶点编号 if (cnt == n) { printf("%d\n", cur); } else { printf("%d ", cur); } // 更新与该顶点相邻的顶点的入度数 Edge* p = graph[cur].next; while (p != NULL) { int next = p->to; graph[next].in_degree--; if (graph[next].in_degree == 0) { q.push(next); } p = p->next; } } // 如果最终输出的顶点数量少于总顶点数,则说明存在环,拓扑排序失败 return cnt == n; } int main() { // 读入顶点数和边数 scanf("%d%d", &n, &m); // 初始化每个顶点的入度数和邻接表 for (int i = 1; i <= n; i++) { graph[i].id = i; graph[i].in_degree = 0; graph[i].next = NULL; } // 构建图 for (int i = 0; i < m; i++) { int from, to; scanf("%d%d", &from, &to); // 添加一条从 from 到 to 的边 Edge* new_edge = (Edge*)malloc(sizeof(Edge)); new_edge->to = to; new_edge->next = graph[from].next; graph[from].next = new_edge; graph[to].in_degree++; // 更新 to 的入度数 } // 拓扑排序 if (!topological_sort()) { printf("图中存在环\n"); } return 0; } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值