
|
| 2 60 0 1 3 |

这道题是经典的综合最短路问题,
综合了 三种最短路方法,1.求路径条数,2.最短路多边权问题,3.求最短路的路径。
这里有个注意的点是,结点人数的的比较应该是更多更好。所以堆排序的时候,注意将结点人数总和多的排在前面。
- #include
- #include
- #include
- #include
- #include
- #define endl '\n'
- #define x first
- #define y second
- #define mk make_pair
- #define int long long
- #define NO puts("NO")
- #define YES puts("YES")
- #define umap unordered_map
- #define INF 0x3f3f3f3f3f3f3f3f
- #define All(x) (x).begin(),(x).end()
- #pragma GCC optimize(3,"Ofast","inline")
- #define ___G std::ios::sync_with_stdio(false),cin.tie(0), cout.tie(0)
- using namespace std;
- const int N = 2e6 + 10;
- using PII = pair<int,int>;
-
- struct Edge
- {
- int a; // 相关结点
- int dis; // 相关最短距离
- int peo; // 相关人数总和
-
- // 构造相关结构体
- inline Edge(int a,int dis,int peo)
- {
- this->a = a;
- this->dis = dis;
- this->peo = peo;
- }
-
- // 重载 < 比较符,建立堆排序规则
- inline bool operator<(const Edge&t)const
- {
- // 优先排序最短路问题,最短路的排前面
- if(dis != t.dis) return dis > t.dis;
- // 如果最短路相同,优先排序人数多的在前面
- return peo < t.peo;
- }
- };
-
- int n,m,k,start,last;
-
- int havePeo[N]; // 记录经过结点的拥有总救援人数
- int dist[N]; // 记录最短路距离
- int peo[N]; // 记录结点城市拥有的救援人数
- bool st[N]; // 标记走动的结点城市
- int tree[N]; // 记录结点城市由哪上一个结点城市相连的,求路径
- int treeSum[N]; // 记录路径条数
- vector<int>path; // 记录路径
-
- int len,sum; // sum 是总救援人数,len 是路径结点个数
-
- // 建立链表
- int h[N],e[N],ne[N],w[N],idx;
- inline void Add(int a,int b,int c)
- {
- e[idx] = b,w[idx] = c,ne[idx] = h[a],h[a] = idx++;
- }
-
- inline void Dijkstra()
- {
- // 初始化最短距离和路径条数和总救援人数
- memset(dist,INF,sizeof dist);
- dist[start] = 0;
- havePeo[start] = 0;
- treeSum[start] = 1;
-
- // 建立堆
- priority_queue
q; -
- // 存储起点相关结构体
- q.push(Edge(start,0,0));
-
- // 开始堆排序的 Dijkstra
- while(q.size())
- {
- // 获取优先走动的相关结构体
- Edge now = q.top();
- q.pop();
-
- int a = now.a; // 获取当前走动的结点
- int dis = now.dis; // 获取当前记录的最短路距离
- int p = now.peo; // 获取当前记录的拥有救援总人数
-
- // 如果当前结点走动过,不必再继续更新走动
- if(st[a]) continue;
- st[a] = true; // 标记当前走动的结点城市
-
- for(int i = h[a];i != -1;i = ne[i])
- {
- int j = e[i]; // 获取走动的结点
- // 如果 j 走动结点的最短路距离方案大于 当前解 a 结点 到 j 结点的距离
- if(dist[j] > dis + w[i])
- {
- dist[j] = dis + w[i];// 更新最短路
- treeSum[j] = treeSum[a]; // 继承路径条数
- tree[j] = a; // 记录当前 j 结点城市是由 a 结点走动得来的
- havePeo[j] = p + peo[j]; // 记录走动到 j 结点路径拥有的总救援人数
- }else // 如果最短距离方案 与当前 a 结点走动到 j 结点最短距离相同
- if(dist[j] == dis + w[i])
- {
- // 累计路径条数
- treeSum[j] += treeSum[a];
-
- // 如果当前 j 结点拥有的总救援人数方案 比 a 到 j 结点的总救援人数 少,那么更新路径
- if(havePeo[j] < p + peo[j])
- {
- havePeo[j] = p + peo[j]; // 更新走动到 j 结点路径拥有的总救援人数
- tree[j] = a; // 更新结点城市路径
- }
- }
- // 存储走动的 j 结点,进行下一次的走动比较
- q.push(Edge(j,dist[j],havePeo[j]));
- }
- }
- }
-
- // 获取最短路的路径
- void getPath(int now)
- {
- // 递归到了起点,开始回溯取路径
- if(now == start)
- {
- // 存储路径
- path.emplace_back(now);
- ++len; // 记录路径点个数
- sum += peo[now]; // 累加救援人数
- return ;
- }
-
- // 递归求路径
- getPath(tree[now]);
-
- // 存储路径
- path.emplace_back(now);
- ++len; // 记录路径点个数
- sum += peo[now]; // 累加救援人数
- }
-
- inline void solve()
- {
- // 初始化链表
- memset(h,-1,sizeof h);
-
- cin >> n >> k >> start >> last;
-
- for(int i = 0;i < n;++i)
- {
- cin >> peo[i];
- }
-
- while(k--)
- {
- int a,b,c;
- cin >> a >> b >> c;
-
- // 相连两个城市
- Add(a,b,c);
- Add(b,a,c);
- }
-
- // Dijkstra 求值
- Dijkstra();
-
- // 由终点回溯递归获取路径
- getPath(last);
-
- // 输出 路径条数,和 救援人员总人数
- cout << treeSum[last] << ' ' << sum << endl;
-
- // 输出救援路径
- for(int i = 0;i < len;++i)
- {
- if(i) cout << ' ';
- cout << path[i];
- }
- }
-
- signed main()
- {
- // freopen("a.txt", "r", stdin);
- ___G;
- int _t = 1;
- // cin >> _t;
- while (_t--)
- {
- solve();
- }
-
- return 0;
- }
