这题的题目看着好吓人,本来想抄抄的,结果发现全都 PA,遂亲自动手......
题目描述
给定有向图无环的边信息,求每个顶点的最早开始时间、最迟开始时间。
// 参考代码
#include <iostream>
#include <vector>
#include <string>
#include <queue>
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<Vertex> vertexes;
vector<vector<int> > adjMat;
int n;
public:
void readVertexes() {
//TODO: 将顶点数读入成员变量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<int> row;
int j=0;
for(; j<n; ++j) {
//TODO: 将0增加到row最后
}
//TODO: 将row增加到adjMat最后
}
}
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.
}
}
void updateEarly(int parentNo, queue<int>& earlyQue) {
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信息
if(!child.hasEnterQueue) {
child.hasEnterQueue = true; //将子结点加入队列
earlyQue.push(j);
}
}
}
void updateLater(int childNo, queue<int>& laterQue) {
//TODO:
}
int getRoot() {
//获取入度为0的顶点
int j=0;
for(; j<n; ++j) {
int i=0;
for(; i<n && adjMat[i][j] == 0; ++i);
if (i>=n) return j; //j has not any in-edges.
}
return -1; //表示没找到
}
int getLeaf() {
//TODO: 获取出度为0的顶点
}
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();
Vertex& root = vertexes[r];
root.hasEnterQueue = true;
root.early = 0;
queue<int> 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<n; ++i) {
vertexes[i].hasEnterQueue = false;
}
}
void findLater() {
//TODO:调用clearEnterQueue,以清除每个顶点的hasEnterQueue=false
//执行关键路径算法,求每个顶点的最迟开始时间。
}
void main() {
readVertexes();
readAdjMatrix();
findEarly();
findLater();
}
};
int main() {
int t=1;
//cin >> t;
while (t--) {
Graph g;
g.main();
}
return 0;
}
辣么长......
AC代码
#include <iostream>
#include <vector>
#include <cmath>
#include <cstring>
using namespace std;
const int N = 100;
struct side // 后继结点
{
int end_pos;
int cost;
};
vector<vector<side>> graph(N); // ---> 正向的邻接表
vector<vector<side>> _graph(N); // <--- 反向的邻接表
int point_num,side_num,max_cost;
int start[N],finish[N];
int in[N],out[N];
bool visit[N]={false};
bool _check()
{
for (int i=0;i<point_num;++i)
if (visit[i]==false)
return true;
return false;
}
void topo_sort()
{
while (_check())
{
for (int i=0;i<point_num;++i)
{
if (in[i]==0 && visit[i]==false) // 挑选入度为0且尚未探索过的点
{
visit[i]=true;
for (auto &cur:graph[i]) // 更新
{
int next_point=cur.end_pos;
in[next_point]--;
start[next_point]=fmax(start[next_point],start[i]+cur.cost);
max_cost=fmax(max_cost,start[next_point]);
}
break;
}
}
}
memset(visit,0,sizeof(visit)); // 初始化
for (int i=0;i<point_num;++i)
finish[i]=max_cost;
while (_check()) // 反着再来一遍
{
for (int i=0;i<point_num;++i)
{
if (out[i]==0 && visit[i]==false)
{
visit[i]=true;
for (auto &cur:_graph[i])
{
int next_point=cur.end_pos;
out[next_point]--;
finish[next_point]=fmin(finish[next_point],finish[i]-cur.cost);
}
break;
}
}
}
}
int main ()
{
cin >> point_num >> side_num;
for (int i=0;i<side_num;++i) // build a graph
{
int begin,end,cost;
cin >> begin >> end >> cost;
graph[begin].push_back({end,cost});
_graph[end].push_back({begin,cost});
in[end]++;
out[begin]++;
}
topo_sort();
for (int i=0;i<point_num;++i)
cout << start[i] << " ";
cout << endl;
for (int i=0;i<point_num;++i)
cout << finish[i] << " ";
cout << endl;
return 0;
}
优化版
#include <iostream>
#include <vector>
#include <queue>
#include <cmath>
#include <algorithm>
using namespace std;
const int N = 100;
struct side
{
int end;
int cost;
};
vector<vector<side>> graph(N);
vector<vector<side>> _graph(N);
int in[N],out[N];
int start[N],finish[N];
int n,m;
void get_critival_path()
{
queue<int> q;
for (int i=0;i<n;++i)
if (in[i]==0)
q.push(i);
while (!q.empty())
{
int cur=q.front();
q.pop();
for (side &i:graph[cur])
{
start[i.end]=fmax(start[i.end],start[cur]+i.cost);
if (--in[i.end]==0)
q.push(i.end);
}
}
int max_val=*max_element(start,start+n);
for (int i=0;i<n;++i)
finish[i]=max_val;
queue<int>().swap(q);
for (int i=0;i<n;++i)
if (out[i]==0)
q.push(i);
while (!q.empty())
{
int cur=q.front();
q.pop();
for (side &i:_graph[cur])
{
finish[i.end]=fmin(finish[i.end],finish[cur]-i.cost);
if (--out[i.end]==0)
q.push(i.end);
}
}
}
int main ()
{
cin >> n >> m;
for (int i=0;i<m;++i)
{
int n1,n2,val;
cin >> n1 >> n2 >> val;
graph[n1].push_back({n2,val});
_graph[n2].push_back({n1,val});
in[n2]++;
out[n1]++;
}
get_critival_path();
for (int i=0;i<n;++i)
cout << start[i] << " ";
cout << endl;
for (int i=0;i<n;++i)
cout << finish[i] << " ";
cout << endl;
return 0;
}
这里是对拓扑排序进行了优化,采用BFS的方式,同样还有DFS的版本
vector<int> print; // 输出数组
bool flag=true; // 判断是否成环,即是否可以构成拓扑序
int n; // n为节点数
void topo_sort()
{
queue<int> q;
for (int i=0;i<n;++i)
if (in[i]==0)
q.push(i);
vector<int> v;
int cnt=0;
while (!q.empty() || !v.empty())
{
if (v.empty())
{
v.emplace_back(q.front());
q.pop();
}
vector<int> tmp;
for (int &cur:v)
{
for (int i=0;i<n;++i)
if (graph[cur][i] && --in[i]==0)
tmp.emplace_back(i);
print.emplace_back(cur);
++cnt;
}
v.swap(tmp);
}
if (cnt!=n)
flag=false;
}
详细可以参考《topological sorting via DFS》