知识点:
知道图谱排序采用的图论原理,知道什么是关键路径
单选题:
2-1在AOE网中,什么是关键路径? (1分)
- 最短回路
- 最长回路
- 从第一个事件到最后一个事件的最短路径
- 从第一个事件到最后一个事件的最长路径
解析:关键路径在AOE网当中表示的就是项目最终完成的顺序,显然一个工程的完成必须是这个工程的最后一个项目完成才行,因此关键路径就是第一个事件(起始事件)到最后一个事件(结束事件)的最长路径(花费的最长时间)。
2-2在拓扑排序算法中用堆栈和用队列产生的结果会不同吗?(1分)
- 是的肯定不同
- 肯定是相同的
- 有可能会不同
- 以上全不对
解析:使用堆栈最后从栈顶到栈底就是逆拓扑的有序序列,而使用队列就是正拓扑的有序序列。
实际上使用堆栈形成的类似于dfs,而使用队列形成的类似于bfs
使用堆栈形成的会将一系列相关的结点存放在一起。
2-3下图为一个AOV网,其可能的拓扑有序序列为: (2分)
- ABCDFEG
- ADFCEBG
- ACDFBEG
- ABDCEFG
解析:根据拓扑排序的原理我们知道第四个是对的。
2-4若将n个顶点e条弧的有向图采用邻接表存储,则拓扑排序算法的时间复杂度是:(1分)
- O(n)
- O(n+e)
- O(n2)
- O(n×e)
解析:首先我们遍历所有的节点找到所有入度为0的节点,加入队列,这个过程就是O(n)。
然后我们遍历每个节点的相邻节点,遍历到的每个节点的入度减一,遇到入度为0就加入队列。
那么第二个过程的时间复杂度就是O(e)
2-5对下图进行拓扑排序,可以得到不同的拓扑序列的个数是: (2分)
- 4
- 3
- 2
- 1
解析:不难知道一共有三种。
2-6已知有向图G=(V, E),其中V = {v1, v2, v3, v4, v5, v6}
,E = {<v1,v2>, <v1,v4>, <v2,v6>, <v3,v1>, <v3,v4>, <v4,v5>, <v5,v2>, <v5,v6>}
。G的拓扑序列是: (2分)
v3, v1, v4, v5, v2, v6
v3, v4, v1, v5, v2, v6
v1, v3, v4, v5, v2, v6
v1, v4, v3, v5, v2, v6
解析:根据边集E我们可以画出AOV网,从而获得拓扑排序。
编程题:
7-1 关键活动 (30 分)
假定一个工程项目由一组子任务构成,子任务之间有的可以并行执行,有的必须在完成了其它一些子任务后才能执行。“任务调度”包括一组子任务、以及每个子任务可以执行所依赖的子任务集。
比如完成一个专业的所有课程学习和毕业设计可以看成一个本科生要完成的一项工程,各门课程可以看成是子任务。有些课程可以同时开设,比如英语和C程序设计,它们没有必须先修哪门的约束;有些课程则不可以同时开设,因为它们有先后的依赖关系,比如C程序设计和数据结构两门课,必须先学习前者。
但是需要注意的是,对一组子任务,并不是任意的任务调度都是一个可行的方案。比如方案中存在“子任务A依赖于子任务B,子任务B依赖于子任务C,子任务C又依赖于子任务A”,那么这三个任务哪个都不能先执行,这就是一个不可行的方案。
任务调度问题中,如果还给出了完成每个子任务需要的时间,则我们可以算出完成整个工程需要的最短时间。在这些子任务中,有些任务即使推迟几天完成,也不会影响全局的工期;但是有些任务必须准时完成,否则整个项目的工期就要因此延误,这种任务就叫“关键活动”。
请编写程序判定一个给定的工程项目的任务调度是否可行;如果该调度方案可行,则计算完成整个工程项目需要的最短时间,并输出所有的关键活动。
输入格式:
输入第1行给出两个正整数N(≤100)和M,其中N是任务交接点(即衔接相互依赖的两个子任务的节点,例如:若任务2要在任务1完成后才开始,则两任务之间必有一个交接点)的数量。交接点按1~N编号,M是子任务的数量,依次编号为1~M。随后M行,每行给出了3个正整数,分别是该任务开始和完成涉及的交接点编号以及该任务所需的时间,整数间用空格分隔。
输出格式:
如果任务调度不可行,则输出0;否则第1行输出完成整个工程项目需要的时间,第2行开始输出所有关键活动,每个关键活动占一行,按格式“V->W”输出,其中V和W为该任务开始和完成涉及的交接点编号。关键活动输出的顺序规则是:任务开始的交接点编号小者优先,起点编号相同时,与输入时任务的顺序相反。
输入样例:
7 8
1 2 4
1 3 3
2 4 5
3 4 3
4 5 1
4 6 6
5 7 5
6 7 2
输出样例:
17
1->2
2->4
4->6
6->7
AC代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 100 + 5;
int n, m;
int Time[maxn][maxn];
int in[maxn], dis[maxn];
int viss[maxn][maxn];
vector<int> Next[maxn];
bool vis[maxn];
struct node
{
int x, y;
node(int x, int y) : x(x), y(y) {}
bool operator < (const node &rhs) const
{
if(x != rhs.x)
return x < rhs.x;
return viss[x][y] > viss[rhs.x][rhs.y];
}
};
vector<node> ans;
void bfs(int pos)
{
queue<int> que;
que.push(pos);
while(!que.empty())
{
int temp = que.front();
que.pop();
if(vis[temp])
continue;
vis[temp] = true;
for(int i = 0; i < Next[temp].size(); i++)
{
ans.push_back(node(Next[temp][i], temp));
que.push(Next[temp][i]);
}
}
}
int main()
{
ios::sync_with_stdio(false);
memset(Time, -1, sizeof(Time));
memset(dis, 0, sizeof(dis));
cin >> n >> m;
for(int i = 0; i < m; i++)
{
int x, y, T;
cin >> x >> y >> T;
Time[x][y] = T;
viss[x][y] = i + 1;
in[y]++;
}
int pos, cnt = 0, max_dis = -1, max_point = -1;
queue<int> que;
for(int i = 1; i <= n; i++)
{
if(!in[i])
que.push(i);
}
while(!que.empty())
{
pos = que.front();
que.pop();
vis[pos] = true;
cnt++;
for(int i = 1; i <= n; i++)
{
if(vis[i] || Time[pos][i] == -1)
continue;
if(--in[i] == 0)
que.push(i);
//Next[i].push_back(pos);
if(dis[i] < dis[pos] + Time[pos][i])
{
dis[i] = dis[pos] + Time[pos][i];
Next[i].clear();
Next[i].push_back(pos);
if(max_dis < dis[i])
{
max_dis = dis[i];
max_point = i;
}
}
else if(dis[i] == dis[pos] + Time[pos][i])
{
dis[i] = dis[pos] + Time[pos][i];
Next[i].push_back(pos);
if(max_dis < dis[i])
{
max_dis = dis[i];
max_point = i;
}
}
}
}
if(cnt < n)
{
cout << 0 << endl;
return 0;
}
cout << max_dis << endl;
memset(vis, false, sizeof(vis));
bfs(max_point);
sort(ans.begin(), ans.end());
for(int i = 0; i < ans.size(); i++)
cout << ans[i].x << "->" << ans[i].y << endl;
return 0;
}
解析:真的是一个很好的题,在不停地wa之中我又一次深刻地明白了什么是关键路径。
关键就是最长路径相等的时候,那个时候就是说明想要完成一个项目,我们必须同时完成多个先修任务才行。