给定有向图无环的边信息,求每个顶点的最早开始时间、最迟开始时间。
// 参考代码
#include
#include
#include
#include
using namespace std;
class Vertex {
public:
int indexNo;
bool hasEnterQueue;
int early;
int later;
Vertex(int indexNo) {
this->indexNo = indexNo;
this->hasEnterQueue = false;
early = -1;
later = 0x7FFFF;
}
void updateEarly(int parentEarly, int edgeValue) {
int newEarly = parentEarly + edgeValue;
if (newEarly > this->early)
this->early = newEarly;
}
void updateLater(int childLater, int edgeValue) {
int newLater = childLater - edgeValue;
if (newLater < this->later)
this->later = newLater;
}
};
class Graph {
public:
vector vertexes;
vector > adjMat;
int n;
public:
void readVertexes() {
//TODO: 将顶点数读入成员变量n
//TODO: 从输入初始化vertexes数组
int i=0;
for(; ivertexes.push_back(v);
}
//为成员变量adjMat创建内存,赋初值
for(i=0; i row;
int j=0;
for(; jadjMat
int edges;
cin >> edges;
int i=0;
int s, t, w; //s源顶点编号,t目的顶点编号,w边长
for(; i& earlyQue) {
int parentEarly = vertexes[parentNo].early; //读入父结点early值
int j=0;
for(; j& laterQue) {
//TODO:
}
int getRoot() {
//获取入度为0的顶点
int j=0;
for(; j=n) return j; //j has not any in-edges.
}
return -1; //表示没找到
}
int getLeaf() {
//TODO: 获取出度为0的顶点
}
void printEarlyLater(bool isEarly) {
int i=0;
for(; i que;
que.push(r);
while(!que.empty()) {
int p = que.front();
que.pop();
updateEarly(p, que);
}
printEarlyLater(true);
}
void clearEnterQueue() {
int i=0;
for(; i> t;
while (t--) {
Graph g;
g.main();
}
return 0;
}
输入
第一行图的顶点总数
第二行边的总数
第三行开始,每条边的时间长度,格式为源结点 目的结点 长度
输出
第一行:第个顶点的最早开始时间
第二行:每个顶点的最迟开始时间
输入样例
9
12
0 1 3
0 2 10
1 3 9
1 4 13
2 4 12
2 5 7
3 6 8
3 7 4
4 7 6
5 7 11
6 8 2
7 8 5
输出样例
0 3 10 12 22 17 20 28 33
0 9 10 23 22 17 31 28 33
——————————————————————————————
这道题的代码确实是错的,这道题的解法是拓扑排序,入度为零的才会更新下一节点,而void updateEarly()会把摸到的节点直接放进那个队列
举个例子:这个图就两条路,一条路上100个点,另一条路直接到终点前一个点。题目的代码开局就会把终点前一个点放进队列,假如终点后面还有个点,那就算100个点过来,也不会过去了。 如图:
其实知道是拓扑排序就很简单了,自己去写就好了。
多起点和多终点其实我一开始就写了,但我其实当时不知道这道题是干什么(想速通,懒得看,老师上课讲这个了,不过我忘了,和课组长交流后才知道惹。早点知道的话,写多起点多终点的时候应该能更早发现)。
小tips:(其实不用看,就是我的代码写法)
因为起始点不一定只有一个(终点同理),所以getroot得加条件。然后发现拓扑排序要找入度为0的点嘛,所以也可以用这个函数。但是后面的点的early已经被弄好了,所以需要另外把起始点们的early初始化一下子。
我这边大胆舍弃毫无用处的队列(这道题代码是遍历找入度为0,所以我就不需要队列存入度为0的点了。当然这样做效率低),然后废物利用Vertex().hasEnterQueue和clearEnterQueue()。
#include
#include
#include
#include
using namespace std;
class Vertex {
public:
int indexNo;
bool hasEnterQueue;
int early;
int later;
Vertex(int indexNo) {
this->indexNo = indexNo;
this->hasEnterQueue = false;
early = -1;
later = 0x3f3f3f3f;//0x7FFFF;
}
void updateEarly(int parentEarly, int edgeValue) {
int newEarly = parentEarly + edgeValue;
if (newEarly > this->early)
this->early = newEarly;
}
void updateLater(int childLater, int edgeValue) {
int newLater = childLater - edgeValue;
if (newLater < this->later)
this->later = newLater;
}
};
class Graph {
public:
vector vertexes;
vector > adjMat;
vector > tmpadjMat;
int n;
int latest;
public:
void readVertexes() {
//TODO: 将顶点数读入成员变量n
cin >> n;
//TODO: 从输入初始化vertexes数组
int i = 0;
for (; i < n; ++i)
{
Vertex v(i);
this->vertexes.push_back(v);
}
//为成员变量adjMat创建内存,赋初值
for (i = 0; i < n; ++i)
{
vector row;
int j = 0;
for (; j < n; ++j)
{
//TODO: 将0增加到row最后
row.push_back(0);//666
}
//TODO: 将row增加到adjMat最后
adjMat.push_back(row);
}
}
void readAdjMatrix() {
//read the adjacent info into this->adjMat
int edges;
cin >> edges;
int i = 0;
int s, t, w; //s源顶点编号,t目的顶点编号,w边长
for (; i < edges; ++i) {
//TODO: 读入s,t,w,并将adjMat的第s行、第t列的值改为w.
cin >> s >> t >> w;
adjMat[s][t] = w;
//adjMat[t][s] = w;
}
tmpadjMat = adjMat;
}
//
void updateEarly(int parentNo) {
int parentEarly = vertexes[parentNo].early; //读入父结点early值
int j = 0;
for (; j < n; ++j) {
int& edgeValue = adjMat[parentNo][j];
if (edgeValue == 0) continue; //若父结点与结点j没有边相连,pass
Vertex& child = vertexes[j];
child.updateEarly(parentEarly, edgeValue); //更新子结点j的early信息
edgeValue = 0;
}
}
void updateLater(int childNo) {
//TODO:
int childLater = vertexes[childNo].later; //读入父结点early值
int j = 0;
for (; j < n; ++j) {
int& edgeValue = adjMat[j][childNo];
if (edgeValue == 0) continue; //若父结点与结点j没有边相连,pass
Vertex& child = vertexes[j];
child.updateLater(childLater, edgeValue); //更新子结点j的early信息
edgeValue = 0;
}
}
int getRoot() {
//获取入度为0的顶点
int j = 0;
for (; j < n; ++j) {
int i = 0;
for (; i < n && adjMat[i][j] == 0; ++i);
if (i >= n && vertexes[j].hasEnterQueue == false) return j; //j has not any in-edges.
}
return -1; //表示没找到
}
int getLeaf() {
//TODO: 获取出度为0的顶点
int j = 0;
for (; j < n; ++j) {
int i = 0;
for (; i < n && adjMat[j][i] == 0; ++i);
if (i >= n && vertexes[j].hasEnterQueue == false) return j;
}
return -1; //表示没找到
}
void printEarlyLater(bool isEarly) {
int i = 0;
for (; i < n; ++i) {
Vertex& v = vertexes[i];
if (isEarly)
{
cout << v.early << " ";
}
else
{
cout << v.later << " ";
}
}
cout << endl;
}
void findEarly() {
//执行关键路径算法,求每个顶点的最早开始时间。
int r = getRoot();
while (r != -1)
{
Vertex& root = vertexes[r];
root.hasEnterQueue = true;
root.early = 0;
r = getRoot();
}
clearEnterQueue();
r = getRoot();
while (r != -1)
{
Vertex& root = vertexes[r];
root.hasEnterQueue = true;
updateEarly(r);
r = getRoot();
}
printEarlyLater(true);
}
void clearEnterQueue() {
int i = 0;
for (; i < n; ++i) {
vertexes[i].hasEnterQueue = false;
}
}
void findLater() {
//TODO:调用clearEnterQueue,以清除每个顶点的hasEnterQueue=false
clearEnterQueue();
adjMat = tmpadjMat;
//执行关键路径算法,求每个顶点的最迟开始时间。
int r = getLeaf();
while (r != -1)
{
Vertex& root = vertexes[r];
root.hasEnterQueue = true;
root.later = root.early;
r = getLeaf();
}
clearEnterQueue();
r = getLeaf();
while (r != -1)
{
Vertex& root = vertexes[r];
root.hasEnterQueue = true;
updateLater(r);
r = getLeaf();
}
printEarlyLater(false);
}
void main() {
readVertexes();
readAdjMatrix();
findEarly();
findLater();
}
};
int main() {
int t = 1;
//cin >> t;
while (t--) {
Graph g;
g.main();
}
return 0;
}
我以为参考代码是对的,然后对称写法,结果只能过两个样例,反复检查代码是没有错误的,再加上对了两个样例,有点自信,然后就去班群里问助教了,助教甩个后台输出就不管了(应该是老师不让多看):
然后今晚打了会CSGO,本来想学动归,可是就想过掉这个简单的拓扑。回来又读了一遍,确信没有问题后,再看了看这个输出:
才想到这个就压根不是拓扑,甚至就是啥也不是,就是错滴。(其实一开始助教发了的时候我只发现有负的这个问题,但是根据这道题拓扑的逻辑根本不会有负的!而我又是封装的代码,最多调用失误,然后调用反复检查没有失误,所以就很疑惑)
网上搜了,没有用本题代码的解法。所以也挺想搞下的。老师们不负责的话,希望学弟们不会因为踩到这个坑而浪费太多时间。(不会吧不会吧就我一个没发现这道题拓扑是错的)