7-13 任务调度的合理性
拓扑排序:
是对有向无环图的顶点的一种排序
在AOV网中,先找到一个没有入度的顶点,然后输出
从网中删除这个顶点和所有以它为起点的有向边
重复以上步骤直至当前AOV网为空或网中不存在无前驱的顶点为止(这是有环图)
假定一个工程项目由一组子任务构成,子任务之间有的可以并行执行,有的必须在完成了其它一些子任务后才能执行。“任务调度”包括一组子任务、以及每个子任务可以执行所依赖的子任务集。
比如完成一个专业的所有课程学习和毕业设计可以看成一个本科生要完成的一项工程,各门课程可以看成是子任务。有些课程可以同时开设,比如英语和C程序设计,它们没有必须先修哪门的约束;有些课程则不可以同时开设,因为它们有先后的依赖关系,比如C程序设计和数据结构两门课,必须先学习前者。
但是需要注意的是,对一组子任务,并不是任意的任务调度都是一个可行的方案。比如方案中存在“子任务A依赖于子任务B,子任务B依赖于子任务C,子任务C又依赖于子任务A”,那么这三个任务哪个都不能先执行,这就是一个不可行的方案。你现在的工作是写程序判定任何一个给定的任务调度是否可行。
输入格式:
输入说明:输入第一行给出子任务数(),子任务按1~编号。随后行,每行给出一个子任务的依赖集合:首先给出依赖集合中的子任务数,随后给出个子任务编号,整数之间都用空格分隔。
输出格式:
如果方案可行,则输出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
/*出错点:
while(!q.empty()),这是一个循环,而不是判断,判断只能进行一次,条件循环,可以一直循环到达到条件为止,差距很大的,好吗!!!
if(d[v]==0) q.push(v),ans++; :把v入队,而不是i,是因为是v的入度为0,i在这个循环中只是为了推进
*/
#include<bits/stdc++.h>
using namespace std;
int n,k,d[110]={0},x;
vector<int>g[110];//这就是一个邻接表,用于表示图的关系,每个元素都是向量类型的对象
bool topo(){
int ans=0;
queue<int>q;
for(int i=1;i<=n;i++){
if(d[i]==0)
q.push(i),ans++;//入度为0,能入队,ans+1
}
while(!q.empty()){
int u=q.front();
q.pop();//及时弹出才能利用q.front()查找入度为0的点的出度
for(unsigned int i=0;i<g[u].size();i++){//这个g[u].size()是获取u的邻接节点出度数量,入度不是邻接节点
int v=g[u][i];//节点u的邻接表中的第i个节点
d[v]--;
if(d[v]==0) q.push(v),ans++;
}
}
if(ans==n)return 1;//说明都能一个接一个的入队,最终都可以入度为0,没有限制,故成功
return 0;
}
int main(){
cin>>n;
for(int i=1;i<=n;i++){ //这个循环的目的是建一个图,之后拓扑判断这个图
cin>>k;
while(k--){
cin>>x;
g[x].push_back(i);//将节点x加入i的邻接列表,从x指向i
d[i]++;
}
}
cout<<topo();
return 0;
}
7-14最短工期
一个项目由若干个任务组成,任务之间有先后依赖顺序。项目经理需要设置一系列里程碑,在每个里程碑节点处检查任务的完成情况,并启动后续的任务。现给定一个项目中各个任务之间的关系,请你计算出这个项目的最早完工时间。
输入格式:
首先第一行给出两个正整数:项目里程碑的数量 ()和任务总数 。这里的里程碑从 0 到 编号。随后 行,每行给出一项任务的描述,格式为“任务起始里程碑 任务结束里程碑 工作时长”,三个数字均为非负整数,以空格分隔。
输出格式:
如果整个项目的安排是合理可行的,在一行中输出最早完工时间;否则输出"Impossible"。
输入样例 1:
9 12
0 1 6
0 2 4
0 3 5
1 4 1
2 4 1
3 5 2
5 4 0
4 6 9
4 7 7
5 7 4
6 8 2
7 8 4
输出样例 1:
18
#include <bits/stdc++.h>
#define endl '\n'
using namespace std;
// 全局变量定义
int a[100][100], s[100], f[100];
int ans, cnt;
void tls(int n, int m) {
while (1) {
int flag = 0;
for (int i = 0; i < n; i++) {
if (s[i] == 0) { // 如果当前节点的入度为0
s[i]--; // 标记该节点已被处理
flag = 1; // 设置标志,表明有节点被处理
cnt++; // 计数已处理节点数加1
for (int j = 0; j < n; j++) {
if (a[i][j] != -1) { // 如果存在从i到j的边
s[j]--; // 减少j的入度
f[j] = max(f[j], f[i] + a[i][j]); // 更新j的最长路径
ans = max(ans, f[j]); // 更新全局最长路径长度
}
}
}
}
if (!flag) break; // 如果没有节点被处理,则退出循环
}
if (n == cnt) cout << ans << endl; // 如果所有节点都被处理,输出最长路径
else cout << "Impossible"; // 否则输出"Impossible"
}
int main() {
int n, m; cin >> n >> m;
int x, y, z;
// 初始化邻接矩阵,将所有边权重设为-1
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
a[i][j] = -1;
}
}
// 读取边信息,并更新邻接矩阵和入度数组
for (int i = 0; i < m; i++) {
cin >> x >> y >> z;
a[x][y] = z;
s[y]++;
}
// 调用拓扑排序和最长路径计算函数
tls(n, m);
return 0;
}