今天学拓扑排序,和dijkstra算法,拓扑排序的思路第一次见,dijkstra算法的题目看起来和昨天写的prim算法很像,但是还是有细微的区别。感觉两个题目看完题解以后也没有那么难实现。
117. 软件构建(卡码网)
这道题用拓扑排序,核心思路很简单,就是不断找入度为0的节点,入度为0的节点就是图的起点,将入度为0的节点找到后,将其放入一个队列中(因为可能会有多个入度为0的节点)然后不断读取队头元素并弹出,读取到了队头元素,再访问依赖于队头元素的其他元素(必须要先安装队头文件才能安装的那些文件),将它们对应的入度-1,如果又有文件的入度被减一后变成0,则将对应的文件也加入队列,只要队列不为空,这个循环就将一直进行下去。
#include<iostream>
#include<vector>
#include<queue>
#include<unordered_map>
using namespace std;
int main(){
int N, M;
cin >> N >> M; //N个文件,M条依赖关系(边)
vector<int> inDegree(N, 0); //统计各个节点的入度
unordered_map<int, vector<int>> My_Map; //统计文件依赖关系
for(int i = 0; i < M; i++){
int s, t;
cin >> s >> t; // s -> t
inDegree[t]++;
My_Map[s].push_back(t); //记录s指向哪些文件
}
//开始BFS
queue<int> My_Queue;
vector<int> result;
for(int i = 0; i < N; i++){
if(inDegree[i] == 0) //将入度为0的节点压入队列中
My_Queue.push(i);
}
while(!My_Queue.empty()){
int current = My_Queue.front();
My_Queue.pop();
result.push_back(current);
vector<int> files = My_Map[current]; //获取current指向的文件
if(files.size() > 0){ //存在依赖文件
for(int i = 0; i < files.size(); i++){
inDegree[files[i]]--;
if(inDegree[files[i]] == 0) //出现了新的起点,需要入队
My_Queue.push(files[i]);
}
}
}
if(result.size() < N) cout << -1 << endl;
else{
for(int i = 0; i < N; i++){
if(i < N - 1) cout << result[i] << " ";
else cout << result[i] << endl;
}
}
return 0;
}
47. 参加科学大会(卡码网)
这道题和昨天prim算法那道题很像,但是二者有一些区别,昨天寻宝的题目是必须将所有的节点都加入最小生成树中,而本题不一定要将所有节点都加入路径中,因为并不是所有节点都可以到达;其次,两道题构造的minDist数组意义不一样,寻宝中的minDist数组的含义是:编号为i的节点与最小生成树之间的最小距离为minDist[i],minDist[i]可能是i号节点与生成树中任意一个节点的距离,取最小值即可,而本题中minDist数组的含义是:编号为i的节点与起始站点的最小距离为minDist[i],二者在定义上也有区别;最后,在更新minDist数组的时候,判定条件也不同,寻宝的判定条件是:当前遍历到的节点不在最小生成树中,且当前节点在current节点加入后与最小生成数的距离小于在current节点加入之前与最小生成树之间的距离,此时才更新minDist数组;而本题更新minDist数组的条件为:当前遍历到的节点不在路径中,且current可以到达当前节点,且当前节点与起点之间的距离(从current节点到当前节点的距离+起点到current节点的距离) < current节点加入路线之前当前节点与起点之间的距离。
除此以外,其他地方都大同小异的。在循环结束后直接判断minDist[end]是不是INT_MAX,如果是的话说明无法到达终点站,直接打印-1,否则打印minDist[end]。
#include<iostream>
#include<vector>
#include <climits>
using namespace std;
int main(){
int N, M;
cin >> N >> M; //N个车站(节点),M条路(边)
vector<vector<int>> grid(N + 1, vector<int> (N + 1, INT_MAX)); //记录整个公交线路图
for(int i = 0; i < M; i++){
int S, E, V;
cin >> S >> E >> V; // S -> E 花费V单位的时间
grid[S][E] = V;
}
vector<bool> visited(N + 1, false); //记录各个公交站点是否已经被访问
vector<int> minDist(N + 1, INT_MAX); //记录各个节点到出发点的最短距离
int start = 1; //起始站点
int end = N; //终点站
minDist[start] = 0;
for(int i = 1; i <= N; i++){ //至多循环N次,添加N个节点
//第一步,选源点到哪个节点近且该节点未被访问过
int current = 0; //当前节点
int minVal = INT_MAX;
for(int j = 1; j <= N; j++){
if(!visited[j] && minDist[j] < minVal){
current = j;
minVal = minDist[j];
}
}
//第二步,该最近节点被标记访问过
visited[current] = true;
//第三步,更新非访问节点到源点的距离(即更新minDist数组)
for(int j = 1; j <= N; j++){
//更新条件:
//1. 当前节点未被访问
//2. 从current节点可以到达当前节点
//3. 当前节点与起点之间的距离(从current节点到当前节点的距离+起点到current节点的距离)
// < current节点加入路线之前当前节点与起点之间的距离
if(!visited[j] && grid[current][j] != INT_MAX
&& minDist[j] > minVal + grid[current][j])
minDist[j] = minVal + grid[current][j];
}
}
if(minDist[end] == INT_MAX) cout << -1 << endl;
else cout << minDist[end] << endl;
return 0;
}
还剩下三天的训练任务!!加油!!曙光不远了!!!