【PTA数据结构 | C语言版】任务调度的合理性

本专栏持续输出数据结构题目集,欢迎订阅。

文章目录

题目

假定一个工程项目由一组子任务构成,子任务之间有的可以并行执行,有的必须在完成了其它一些子任务后才能执行。“任务调度”包括一组子任务、以及每个子任务可以执行所依赖的子任务集。

比如完成一个专业的所有课程学习和毕业设计可以看成一个本科生要完成的一项工程,各门课程可以看成是子任务。有些课程可以同时开设,比如英语和 C 程序设计,它们没有必须先修哪门的约束;有些课程则不可以同时开设,因为它们有先后的依赖关系,比如 C 程序设计和数据结构两门课,必须先学习前者。

但是需要注意的是,对一组子任务,并不是任意的任务调度都是一个可行的方案。比如方案中存在“子任务 a 依赖于子任务 b,子任务 b 依赖于子任务 c,子任务 c 又依赖于子任务 a”,那么这三个任务哪个都不能先执行,这就是一个不可行的方案。你现在的工作是写程序判定任何一个给定的任务调度是否可行。

输入格式:
输入说明:输入第一行给出子任务数 n(≤100),子任务按 1~ n 编号。随后 n 行,每行给出一个子任务的依赖集合:首先给出依赖集合中的子任务数 k ,随后给出 k n编号。随后n行,每行给出一个子任务的依赖集合:首先给出依赖集合中的子任务数k,随后给出k n编号。随后n行,每行给出一个子任务的依赖集合:首先给出依赖集合中的子任务数k,随后给出k 个子任务编号,整数之间都用空格分隔。

输出格式:
如果方案可行,则输出 1,否则输出 0。

输入样例1:
12
0
0
2 1 2
0
1 4
1 5
2 3 6
1 3
2 7 8
1 7
1 10
1 7

输出样例1:
1

输入样例2:
5
1 4
2 1 4
2 2 5
1 3
0

输出样例2:
0

代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_TASKS 101  // 子任务编号从1开始,最大100

// 邻接表节点结构
typedef struct Node {
    int task;           // 依赖的子任务编号
    struct Node* next;  // 下一个节点
} Node;

// 创建新节点
Node* createNode(int task) {
    Node* newNode = (Node*)malloc(sizeof(Node));
    newNode->task = task;
    newNode->next = NULL;
    return newNode;
}

int main() {
    int n;  // 子任务数量
    scanf("%d", &n);
    
    // 邻接表:adj[u]存储所有依赖于u的子任务v(即v依赖u,u必须在v之前完成)
    Node* adj[MAX_TASKS] = {NULL};
    int inDegree[MAX_TASKS] = {0};  // 入度:每个任务依赖的任务数量
    
    // 读取每个子任务的依赖集合
    for (int v = 1; v <= n; v++) {
        int k;
        scanf("%d", &k);
        inDegree[v] = k;  // 该任务依赖k个其他任务
        
        // 对于每个依赖的任务u,添加一条u→v的边,表示u完成后才能执行v
        for (int i = 0; i < k; i++) {
            int u;
            scanf("%d", &u);
            Node* newNode = createNode(v);
            newNode->next = adj[u];
            adj[u] = newNode;
        }
    }
    
    // 拓扑排序:使用队列存储入度为0的任务
    int queue[MAX_TASKS];
    int front = 0, rear = 0;
    
    // 初始化队列:将所有入度为0的任务入队
    for (int i = 1; i <= n; i++) {
        if (inDegree[i] == 0) {
            queue[rear++] = i;
        }
    }
    
    int count = 0;  // 记录已处理的任务数量
    
    // 处理队列中的任务
    while (front < rear) {
        int u = queue[front++];  // 取出一个入度为0的任务
        count++;                 // 计数加1
        
        // 遍历所有依赖于u的任务v
        Node* current = adj[u];
        while (current != NULL) {
            int v = current->task;
            inDegree[v]--;  // v的入度减1(因为u已完成)
            
            // 如果v的入度变为0,说明其所有依赖都已完成,可以入队
            if (inDegree[v] == 0) {
                queue[rear++] = v;
            }
            
            current = current->next;
        }
    }
    
    // 如果所有任务都被处理,则调度可行;否则存在环,不可行
    printf("%d\n", (count == n) ? 1 : 0);
    
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

秋说

感谢打赏

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值