拓扑图学习指南
前置芝士
拓扑算法经常结合于其他高级算法,注重培养拓扑的核心思想,解决实际问题。
拓扑排序
拓扑排序是一种在有向无环图(DAG)中对节点进行排序的算法,其中每个节点表示一个任务或活动,并且边表示任务之间的依赖关系。
在拓扑排序中,排在前面的节点不依赖于排在后面的节点,因此可以按照一定的顺序依次执行这些任务或活动。
Kahn算法(卡恩)
时间复杂度:O(N+M)
[算法流程]
- 初始化一个队列(FIFO)和一个存储结果的列表。
- 统计每个节点的入度(即节点有多少个前驱节点),并将入度为0的节点加入队列。
- 当队列非空时,执行以下步骤:
a. 从队列中取出一个节点。
b. 将该节点添加到结果列表。
c. 遍历该节点的所有邻居节点:- 将邻居节点的入度减1。
- 若邻居节点的入度为0,将其加入队列。
- 遍历结束后,如果结果列表的长度等于图中节点的个数,则拓扑排序成功;否则,图中存在环,无法进行拓扑排序。
- 求字典序最小的拓扑序,将Kahn算法中的队列转为优先队列(小根堆)
最长路
[problem description]
设 \(G\) 为有 \(n\) 个顶点的带权有向无环图,\(G\) 中各顶点的编号为 \(1\) 到 \(n\),请设计算法,计算图 \(G\) 中 \(1, n\) 间的最长路径。
[input]
输入的第一行有两个整数,分别代表图的点数 \(n\) 和边数 \(m\)。
第 \(2\) 到第 \((m + 1)\) 行,每行 \(3\) 个整数 \(u, v, w\)(\(u<v\)),代表存在一条从 \(u\) 到 \(v\) 边权为 \(w\) 的边。
[output]
输出一行一个整数,代表 \(1\) 到 \(n\) 的最长路。
若 \(1\) 无法到达 \(n\),请输出 \(-1\)。
\(1 \leq n \leq 1500\),\(0 \leq m \leq 5 \times 10^4\),\(1 \leq u, v \leq n\),\(-10^5 \leq w \leq 10^5\)。
[solved]
int n,m;
const int N=1510;
vector<int> e[N],d[N];
int w[N],din[N],f[N];
queue<int> q;
void solve(){
cin>>n>>m;
for(int i=1;i<=m;i++){
int x,y,w;
cin>>x>>y>>w;
e[x].push_back(y);
d[x].push_back(w);
din[y]++;
}
for(int i=2;i<=n;i++){
f[i]=-1e9;
if(!din[i]) q.push(i);
}
while(!q.empty()){
int x=q.front();
q.pop();
for(int i=0;i<e[x].size();i++){
if(!--din[e[x][i]]) q.push(e[x][i]);
}
}
q.push(1);
while(!q.empty()){
int x=q.front();
q.pop();
for(int i=0;i<e[x].size();i++){
int v=e[x][i];
if(f[v]<f[x]+d[x][i]) f[v]=f[x]+d[x][i];
if(!--din[v]) q.push(v);
}
}
if(f[n]==-1e9) cout<<"-1";
else cout<<f[n];
}