在一幅有向图中有 N N N个顶点和 M M M条连接任意两点的边,每条边都是有向带权的且权值非负。求从一点出发到各点的最短路径长度及最短路径。
第一行依次给出 N N N(顶点的个数)、 M M M(有向边的个数)和 S S S(起始点)。接下来 M M M行依次给出各个有向边的起点编号、终点编号和权重。顶点编号从 0 0 0到 N − 1 N-1 N−1。
第一行给出 N N N个整数,按照各个顶点的编号依次给出从起始点到该点的最短路径的长度,中间以空格分隔,末尾没有多余空格。接下来 N N N行按照各个顶点的编号依次给出从起始点到该点的最短路径,路径中的各点以“->”作为分隔。
6 8 0
0 1 1
0 3 4
0 4 4
1 3 2
2 5 1
3 2 2
3 4 3
4 5 3
0 1 5 3 4 6
0
0->1
0->1->3->2
0->1->3
0->4
0->1->3->2->5
若从 A A A到 C C C的最短路径中要经过 B B B点,那么从 A A A到 B B B的这段路径就是从 A A A到 B B B的最短路径,这也就满足了最优子结构的性质。我们使用一个长度数组来存储起始点到各点的距离。每次从其中选取为访问过的、距离起始点最近的点作为一个跳板,通过此跳板查看是否可以使起始点到达其它点的距离更近,然后更新长度数组。每次使用完跳板后都对其做标记,直至所有可到达的点都被做上标记。
#include
#include
#include
using namespace std;
int main(){
struct node{///定义邻接表中的节点
size_t terminus;
size_t length;
};
size_t number,road,start;
cin >> number >> road >> start;
vector<vector<node> > adjacency(number);///建立邻接表
for(size_t i=0;i<road;i++){
size_t left,right,length;
cin >> left >> right >> length;
adjacency[left].push_back({right,length});///向邻接表中放入有向边
}
vector<bool> mark(number,false);///标记已经访问过的点
vector<size_t> table(number,UINT_MAX);///记录出发点到各点的距离初始化为无穷大
table[start] = 0;///出发点到出发点距离为零
size_t next = start;///初始化下一次访问的点为出发点
size_t length = 0;///初始化到下一次访问的点的距离为零
do{
for(node temp:adjacency[next]){///枚举当前访问点有向连接的各点
table[temp.terminus] = min(length+temp.length,table[temp.terminus]);///通过该点到其它点距离是否可以更近
}
mark[next] = true;///标记该点已经访问过
length = UINT_MAX;///初始化到下一次访问点的距离为无穷大
for(size_t i=0;i<number;i++){
if(!mark[i]&&table[i]<length){///选取未访问过的、到该点最小距离的点
length = table[i];///记录到该点的距离
next = i;///记录该点的标号
}
}
}while(length<UINT_MAX);///已经访问过该点所在连通图的所有点
cout << table[0];
for(size_t i=1;i<table.size();i++){
cout << ' ' << table[i];
}
cout << endl;
return 0;
}
#include
#include
#include
#include
#include
using namespace std;
int main(){
struct node{///定义邻接表中的节点
size_t terminus;
size_t length;
};
size_t number,road,start;
cin >> number >> road >> start;
vector<vector<node> > adjacency(number);///建立邻接表
for(size_t i=0;i<road;i++){
size_t left,right,length;
cin >> left >> right >> length;
adjacency[left].push_back({right,length});///向邻接表中放入有向边
}
vector<bool> mark(number,false);///标记已经访问过的点
vector<size_t> table(number,UINT_MAX);///记录出发点到各点的距离初始化为无穷大
table[start] = 0;///出发点到出发点距离为零
vector<size_t> last(number);///记录达到某点的上一点
for(size_t i=0;i<last.size();i++){
last[i] = i;///初始化各点的上一点都是自己
}
size_t next = start;///初始化下一次访问的点为出发点
size_t length = 0;///初始化到下一次访问的点的距离为零
do{
for(node temp:adjacency[next]){///枚举next点有向连接的temp
if(length+temp.length<table[temp.terminus]){///通过next点到temp.id点是否可以更近
table[temp.terminus] = length+temp.length;///更新到temp.id点的距离
last[temp.terminus] = next;///记录是通过next点到达temp.id的
}
}
mark[next] = true;///标记该点已经访问过
length = UINT_MAX;///初始化到下一次访问点的距离为无穷大
for(size_t i=0;i<number;i++){
if(!mark[i]&&table[i]<length){///选取未访问过的、到该点最小距离的点
length = table[i];///记录到该点的距离
next = i;///记录该点的标号
}
}
}while(length<UINT_MAX);///已经访问过该点所在连通图的所有点
cout << table[0];
for(size_t i=1;i<table.size();i++){
cout << ' ' << table[i];
}
cout << endl;
stack<size_t> path;///建立栈用于记录路径
for(size_t i=0;i<number;i++){
path.push(i);
while(path.top()!=last[path.top()]){///起始点的上一点一定是自己,找到起始点则终止
path.push(last[path.top()]);///通过各点的上一点找到整条路经
}
while(path.size()>1){
cout << path.top() << "->";
path.pop();
}
cout << path.top() << endl;
path.pop();
}
return 0;
}
#include
#include
#include
#include
using namespace std;
int main(){
struct node{///定义邻接表中的节点
size_t terminus;
size_t length;
};
size_t number,road,start;
cin >> number >> road >> start;
vector<vector<node> > adjacency(number);///建立邻接表
for(size_t i=0;i<road;i++){
size_t left,right,length;
cin >> left >> right >> length;
adjacency[left].push_back({right,length});///向邻接表中放入有向边
}
vector<bool> mark(number,false);///标记已经访问过的点
vector<size_t> table(number,UINT_MAX);///记录出发点到各点的距离初始化为无穷大
table[start] = 0;///出发点到出发点距离为零
struct point{
size_t id;
size_t length;
bool operator<(const point &another)const{
return length > another.length;///构建小顶堆
}
};
priority_queue<point> group;///使用优先队列来产生下次要访问的点
group.push({start,table[start]});
while(!group.empty()){
point next = group.top();
group.pop();
if(mark[next.id]){///选取未访问过的、距离起始点距离最小的点
continue;
}
for(node temp:adjacency[next.id]){///枚举当前访问点有向连接的各点
if(next.length+temp.length<table[temp.terminus]){
table[temp.terminus] = next.length+temp.length;
group.push({temp.terminus,table[temp.terminus]});///向队列中放入更新后的距离
}
}
mark[next.id] = true;///标记该点已经访问过
}
cout << table[0];
for(size_t i=1;i<table.size();i++){
cout << ' ' << table[i];
}
cout << endl;
return 0;
}
/** \brief
*
* \param const vector > > &adj /// 邻接表,adj[sta][].first为点sta到点adj[sta][].second的距离
* \param const int sta /// 起始点
* \param vector &tab /// tab[tem] 为sta到tem的距离
* \return void
*
*/
void dijkstra(const vector<vector<pair<int, int> > > &adj, const int sta, vector<int> &tab){
tab.resize(adj.size(), INT_MAX); /// 初始化
tab[sta] = 0;
priority_queue<pair<int, int>, vector<pair<int, int> >, greater<pair<int, int> > > que; /// 小顶堆
que.push({tab[sta], sta});
vector<bool> mar(adj.size(), false); /// 标记某点已访问
size_t cou = 0; /// 已访问点计数
while(!que.empty()){
pair<int, int> mid = que.top(); /// 获取未访问点中距离最小的点
que.pop();
if(mar[mid.second]) continue;
for(pair<int, int> edg: adj[mid.second]){ /// 尝试松弛所有与该点连接的点
int len = mid.first + edg.first;
if(len < tab[edg.second]){
que.push({len, edg.second}); /// 将松弛成功的点放入堆中
tab[edg.second] = len;
}
}
mar[mid.second] = true;
if(++cou == adj.size()) break;
}
}
/** \brief
*
* \param const vector > > &adj /// 邻接表,adj[sta][].first为点sta到点adj[sta][].second的距离
* \param const int sta /// 起始点
* \param vector > > &tab /// tab[tem].first 为sta到tem的距离,tab[tem].second 为到达tem的路径中tem的上一个点(可能有多条路径,故该参数为一个点集)
* \return void
*
*/
void dijkstra(const vector<vector<pair<int, int> > > &adj, const int sta, vector<pair<int, vector<int> > > &tab){
tab.resize(adj.size(), {INT_MAX, {}}); /// 初始化
tab[sta].first = 0;
priority_queue<pair<int, int>, vector<pair<int, int> >, greater<pair<int, int> > > que; /// 小顶堆
que.push({tab[sta].first, sta});
vector<bool> mar(adj.size(), false); /// 标记某点已访问
size_t cou = 0; /// 已访问点计数
while(!que.empty()){
pair<int, int> mid = que.top(); /// 获取未访问点中距离最小的点
que.pop();
if(mar[mid.second]) continue;
for(pair<int, int> edg: adj[mid.second]){ /// 尝试松弛所有与该点连接的点
int len = mid.first + edg.first;
if(len < tab[edg.second].first){
que.push({len, edg.second}); /// 将松弛成功的点放入堆中
tab[edg.second].first = len;
tab[edg.second].second.clear();
tab[edg.second].second.push_back(mid.second);
}else if(len == tab[edg.second].first){
tab[edg.second].second.push_back(mid.second);
}
}
mar[mid.second] = true;
if(++cou == adj.size()) break;
}
}