PAT(甲级)2019年秋季考试 7-4 Dijkstra Sequence (30 分)
题目描述:
Dijkstra‘s algorithm is one of the very famous greedy algorithms. It is used for solving the single source shortest path problem which gives the shortest paths from one particular source vertex to all the other vertices of the given graph. It was conceived by computer scientist Edsger W. Dijkstra in 1956 and published three years later.
In this algorithm, a set contains vertices included in shortest path tree is maintained. During each step, we find one vertex which is not yet included and has a minimum distance from the source, and collect it into the set. Hence step by step an ordered sequence of vertices, let‘s call it Dijkstra sequence, is generated by Dijkstra‘s algorithm.
On the other hand, for a given graph, there could be more than one Dijkstra sequence. For example, both { 5, 1, 3, 4, 2 } and { 5, 3, 1, 2, 4 } are Dijkstra sequences for the graph, where 5 is the source. Your job is to check whether a given sequence is Dijkstra sequence or not.
译:Dijkstra 算法是很有名的贪心算法之一。它用于解决单源最短路径问题,该问题给出从一个特定源顶点到给定图的所有其他顶点的最短路径。它由计算机科学家 Edsger W. Dijkstra 于 1956 年构思,并于三年后发表。
在该算法中,维护一个包含最短路径树中包含的顶点的集合。 在每一步中,我们找到一个尚未包含的顶点,并且与源的距离最小,并将其收集到集合中。 因此,一个有序的顶点序列,我们称之为 Dijkstra 序列,是由 Dijkstra 算法逐步生成的。
另一方面,对于给定的图,可能有多个 Dijkstra 序列。 例如,{ 5, 1, 3, 4, 2 } 和 { 5, 3, 1, 2, 4 } 都是图的 Dijkstra 序列,其中 5 是源。 你的工作是检查给定的序列是否是 Dijkstra 序列。
Input Specification (输入说明):
Each input file contains one test case. For each case, the first line contains two positive integers Nv (≤10 3) and Ne (≤10 5), which are the total numbers of vertices and edges, respectively. Hence the vertices are numbered from 1 to Nv.
Then Ne lines follow, each describes an edge by giving the indices of the vertices at the two ends, followed by a positive integer weight (≤100) of the edge. It is guaranteed that the given graph is connected.
Finally the number of queries, K, is given as a positive integer no larger than 100, followed by K lines of sequences, each contains a permutationof the Nv vertices. It is assumed that the first vertex is the source for each sequence.
All the inputs in a line are separated by a space.
译:每个输入文件包含一个测试用例,对于每种情况,第一行包含两个整数Nv (≤10 3) 和 Ne (≤10 5), 分别代表顶点总数和边的总数。因此,顶点从 1 到 N 编号。
接下来 Ne 行,每行给出一条边的描述:边的端点的索引号以及一个表示边权 weight (≤100) 的整数。题目保证给定的图是联通的。
最后是查询次数 K ,一个不超过 100 的正整数,接下来 K 行,每行包含一个 Nv 个顶点的排列,默认第一个顶点是每个序列的起点。
所有在一行中的输入数字都用一个空格隔开。
Output Specification (输出说明):
For each of the K sequences, print in a line Yes
if it is a Dijkstra sequence, or No
if not.
译:对于 K 个序列中的每一个序列,如果它是一个 Dijkstra 序列,则打印 Yes
否则打印 No
。
Sample Input (样例输入):
5 7
1 2 2
1 5 1
2 3 1
2 4 1
2 5 2
3 5 1
3 4 1
4
5 1 3 4 2
5 3 1 2 4
2 3 4 5 1
3 2 1 5 4
Sample Output (样例输出):
Yes
Yes
Yes
No
The Idea:
题目意思很简单,可惜考试的时候没有做出来,其实就是规定一个图,然后给定一些顶点访问序列,判断该序列是否可以是 Dijkstra
算法过程中遍历产生的序列。
其实可以套 Dijkstra
算法的模板,奈何我太菜了,想不起来,唉!
The Codes:
#include<bits/stdc++.h>
using namespace std ;
const int maxn = 1010 ;
const int inf = 0x3f3f3f3f ;
int g[maxn][maxn] ;
int d[maxn] ;
bool vis[maxn] ;
int nv , ne , k ;
bool isDijkstraSeq(vector<int> num){
// 初始化
fill(d , d + maxn , inf) ;
memset(vis , false, sizeof(vis)) ;
d[num[1]] = 0 ;
// n - 1 重循环
for(int i = 1 ; i < nv ; i ++){
int minv = inf ;
set<int> st ;
for(int j = 1 ; j <= nv ; j ++){ // 找当前最小的 d[j]
if(vis[j] == false && d[j] < minv){ // 更新 d[j]
st.clear() ;
st.insert(j) ;
minv = d[j] ;
}else if(vis[j] == false && d[j] == minv) st.insert(j) ; // 相同 d[j] 加入备选
}
int u = num[i] ;
if(!st.count(u)) return false ;
vis[u] = true ;
// 松弛
for(int v = 1 ; v <= nv ; v ++){
if(vis[v] == false && g[u][v] != inf && d[u] + g[u][v] < d[v]){
d[v] = d[u] + g[u][v] ;
}
}
}
return true ;
}
int main(){
fill(g[0] , g[0] + maxn * maxn , inf) ;
scanf("%d%d" , &nv , &ne) ;
int a , b , c ;
for(int i = 0 ; i < ne ; i ++){ // 输入图
scanf("%d%d%d" , &a , &b , &c) ;
g[b][a] = g[a][b] = c ;
}
scanf("%d" , &k) ;
for(int i = 0 ; i < k ; i ++){
vector<int> num(nv + 1) ;
for(int j = 1 ; j <= nv ; j ++) scanf("%d" , &num[j]) ;
if(isDijkstraSeq(num)) printf("Yes\n") ;
else printf("No\n") ;
}
return 0 ;
}