题解 P7100 【[w3R1] 团】

Chinshyo

2020-11-29 19:11:02

Solution

**单源最短路的问题** # $ First $ : 建图 建图的过程题目告诉我们,对于每一个$ i,j $,如果$(T_i,W_i),(T_j,W_i)∈S$,那么$ T_i,T_j $之间就会有权值为$ W_i+W_j $的无向边。那么根据题意我们可以知道,**只要在集合$ S $内的任意两点都两两相连**。所以通过样例我们可以构造出来的如图: ![](https://cdn.luogu.com.cn/upload/image_hosting/t5c4t7vy.png) 有了这些,就可以直接通过双重循环来将图中任意两点相连。配合输入的局部代码: ```cpp int n, k; cin >> n >> k; int t, a, b; for(int i = 1; i <= k; i++) { cin >> t; vector < pair<int, int> > Set; for(int j = 1; j <= t; j++){ cin >> a >> b; Set.push_back(make_pair(a, b)); } for(int j = 0; j < t; j++){ for(int k = j + 1; k < t; k++){ graph[Set[j].first].push_back((node){Set[k].first, Set[k].second + Set[j].second}); graph[Set[k].first].push_back((node){Set[j].first, Set[k].second + Set[j].second}); } } } ``` # $ Second $ :最短路求解 我们所知的最流行的两种最短路求解算法就是$ Dijkstra $了。 ### 算法简介 在最开始时,只有起点的最短距离是确定的。在尚未使用过的顶点中,距离的$d[i]$最小顶点就是最短路径已经确定的顶点。因为本题不存在负边,所以不会再之后的更新中变小。**他的局限就是无法考虑负权**。 ![](https://cdn.luogu.com.cn/upload/image_hosting/uy0rhkxt.png) 如图,箭头所指节点的最短路径已知是$ 0 $,所以我们就可以通过他来更新他所指向的所有边。 ### 局部代码 ```cpp while(!pr.empty()){ int now = pr.top().second;//记录节点当时的编号 pr.pop(); if(visited[now] == true){//判重 continue; } visited[now] = true; for(int i = 0; i < graph[now].size(); i++){ int next = graph[now][i].to;//下一个节点 if(dis[now] + graph[now][i].cost < dis[next]){//如果找到更优解 dis[next] = dis[now] + graph[now][i].cost;//更新最短路 pr.push(make_pair(dis[next], next));//压入小根堆 } } } ``` # $ Third $:收尾 本题最终是要求输出到每个节点的最短距离。所以我们可以直接将整个$dis$数组输出。 ```cpp for(int i = 1; i <= n; i++){ cout << dis[i] << ' '; } ``` # $ Forth $:终了 · 贴代码 ```cpp #include<bits/stdc++.h> using namespace std; const int MaxN = 200005; struct node{ int to, cost; }; vector <node> graph[MaxN]; bool visited[MaxN]; int dis[MaxN]; int main(){ int n, k; cin >> n >> k; int t, a, b; for(int i = 1; i <= k; i++) { cin >> t; vector < pair<int, int> > Set; for(int j = 1; j <= t; j++){//无脑输入 cin >> a >> b; Set.push_back(make_pair(a, b)); } for(int j = 0; j < t; j++){ for(int k = j + 1; k < t; k++){ graph[Set[j].first].push_back((node){Set[k].first, Set[k].second + Set[j].second}); graph[Set[k].first].push_back((node){Set[j].first, Set[k].second + Set[j].second}); } } } priority_queue< pair<int, int> , vector< pair<int, int> > , greater< pair<int, int> > > pr; //first->cost second->node pr.push(make_pair(0, 1)); for(int i = 1; i <= n; i++){ dis[i] = INT_MAX; } dis[1] = 0; memset(visited, false, sizeof(visited)); while(!pr.empty()){ int now = pr.top().second;//记录节点当时的编号 pr.pop(); if(visited[now] == true){//判重 continue; } visited[now] = true; for(int i = 0; i < graph[now].size(); i++){ int next = graph[now][i].to;//下一个节点 if(dis[now] + graph[now][i].cost < dis[next]){//如果找到更优解 dis[next] = dis[now] + graph[now][i].cost;//更新最短路 pr.push(make_pair(dis[next], next));//压入小根堆 } } } for(int i = 1; i <= n; i++){//输出 cout << dis[i] << ' '; } return 0; } ```