迪杰斯特拉算法主要用于解决单源最短路问题,主要有两种,朴素版和堆优化版,数据量较大时用堆优化版。
迪杰斯特拉朴素版:
#include<bits/stdc++.h>
#include<unordered_map>
using namespace std;
#define int long long //可能会超时
#define PII pair<int,int>
const int INF = 0x3f3f3f3f, mod = 998244353;
const int N = 505;
int n, m;
int g[N][N], mincost[N],vis[N];
void dijk(int s) {
mincost[s] = 0;
while (1) {
int j = -1;
for (int i = 1;i <= n;i++) {
if(!vis[i])
if (j == -1 || mincost[i] < mincost[j]) j = i;
}
if (j == -1) break;
vis[j] = 1;
for (int i = 1;i <= n;i++)
mincost[i] = min(mincost[i], mincost[j] + g[j][i]);
}
}
signed main()
{
ios::sync_with_stdio, cin.tie(0), cout.tie(0);
cin >> n >> m;
memset(mincost, INF, sizeof mincost);
memset(g, INF, sizeof g);
while (m--) {
int a, b,c;
cin >> a >> b>>c;
g[a][b] = min(g[a][b],c);
}
dijk(1);
cout << (mincost[n] > INF ? -1 : mincost[n]);
return 0;
}
堆优化版迪杰斯特拉:
#include<bits/stdc++.h>
#include<unordered_map>
using namespace std;
#define int long long //可能会超时
#define PII pair<int,int>
const int INF = 0x3f3f3f3f, mod = 998244353;
const int N = 2e5;
int h[N], e[N * 2], ne[2 * N], w[N * 2], vis[N], mincost[N];
int n, m, idx;
priority_queue<PII, vector<PII>, greater<PII>>q;
void add(int a, int b, int c) {
e[idx] = b;
ne[idx] = h[a];
w[idx] = c;
h[a] = idx++;
}
void dijk(int s) {
mincost[s] = 0;
q.push({ 0,s });
while (q.size()) {
auto it = q.top().second;
q.pop();
if (vis[it]) continue;
vis[it] = 1;
for (int i = h[it];i != -1;i = ne[i]) {
int j = e[i];
if (mincost[j] > mincost[it] + w[i]) {
mincost[j] = mincost[it] + w[i];
q.push({ mincost[j],j });
}
}
}
}
signed main()
{
ios::sync_with_stdio, cin.tie(0), cout.tie(0);
cin >> n >> m;
memset(h, -1, sizeof h);
memset(mincost, INF, sizeof mincost);
while (m--) {
int a, b, c;
cin >> a >> b >> c;
add(a, b, c);
}
dijk(1);
cout << (mincost[n] >= INF ? -1 : mincost[n]);
return 0;
}
以下是pintia上几道关于朴素版迪杰斯特拉的题目,整体思路都大差不差:
1.
L2-001 紧急救援
作为一个城市的应急救援队伍的负责人,你有一张特殊的全国地图。在地图上显示有多个分散的城市和一些连接城市的快速道路。每个城市的救援队数量和每一条连接两个城市的快速道路长度都标在地图上。当其他城市有紧急求助电话给你的时候,你的任务是带领你的救援队尽快赶往事发地,同时,一路上召集尽可能多的救援队。
输入格式:
输入第一行给出4个正整数N、M、S、D,其中N(2≤N≤500)是城市的个数,顺便假设城市的编号为0 ~ (N−1);M是快速道路的条数;S是出发地的城市编号;D是目的地的城市编号。
第二行给出N个正整数,其中第i个数是第i个城市的救援队的数目,数字间以空格分隔。随后的M行中,每行给出一条快速道路的信息,分别是:城市1、城市2、快速道路的长度,中间用空格分开,数字均为整数且不超过500。输入保证救援可行且最优解唯一。
输出格式:
第一行输出最短路径的条数和能够召集的最多的救援队数量。第二行输出从S到D的路径中经过的城市编号。数字间以空格分隔,输出结尾不能有多余空格。
输入样例:
4 5 0 3
20 30 40 10
0 1 1
1 3 2
0 3 3
0 2 2
2 3 2
输出样例:
2 60
0 1 3
AC代码:
#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
#define int long long //可能会超时
#define PII pair<int,int>
const int INF = 0x3f3f3f3f, mod = 998244353;
const int N = 1005;
int g[N][N], vis[N], pre[N], mincost[N], cnt[N], sum[N], a[N];
int n, m, s, d;
stack<int>st;
/*
cnt[]:最短路径条数
sum[]:能召集到最多的救援队数量
a[]:每个城市的救援队数量
*/
void dijk(int s) {
mincost[s] = 0;
sum[s] = a[s];
cnt[s] = 1;
while (1) {
int j = -1;
for (int i = 0;i < n;i++) {
if (!vis[i])
if (j == -1 || mincost[i] < mincost[j]) j = i;
}
if (j == -1) break;
vis[j] = 1;
for (int i = 0;i < n;i++) {
if (mincost[i] > mincost[j] + g[j][i]) {
mincost[i] = mincost[j] + g[j][i];
cnt[i] = cnt[j];
sum[i] = sum[j] + a[i];
pre[i] = j;
}
else if (mincost[i] == mincost[j] + g[j][i]) {
cnt[i] += cnt[j];
if (sum[i] < sum[j] + a[i]) {
sum[i] = sum[j] + a[i];
pre[i] = j;
}
}
}
}
}
signed main()
{
ios::sync_with_stdio, cin.tie(0), cout.tie(0);
memset(g, INF, sizeof g);
memset(mincost, INF, sizeof mincost);
cin >> n >> m >> s >> d;
for (int i = 0;i < n;i++) cin >> a[i];
while (m--) {
int a, b, c;
cin >> a >> b >> c;
g[a][b] = g[b][a] = c;
}
dijk(s);
cout << cnt[d] << ' ' << sum[d] << endl;
cout << s;
while (d - s) {
st.push(d);
d = pre[d];
}
while (st.size()) {
cout << ' ' << st.top();
st.pop();
}
return 0;
}
2.
甲级 1003 Emergency
As an emergency rescue team leader of a city, you are given a special map of your country. The map shows several scattered cities connected by some roads. Amount of rescue teams in each city and the length of each road between any pair of cities are marked on the map. When there is an emergency call to you from some other city, your job is to lead your men to the place as quickly as possible, and at the mean time, call up as many hands on the way as possible.
Input Specification:
Each input file contains one test case. For each test case, the first line contains 4 positive integers: N (≤500) - the number of cities (and the cities are numbered from 0 to N−1), M - the number of roads, C1 and C2 - the cities that you are currently in and that you must save, respectively. The next line contains N integers, where the i-th integer is the number of rescue teams in the i-th city. Then M lines follow, each describes a road with three integers c1, c2 and L, which are the pair of cities connected by a road and the length of that road, respectively. It is guaranteed that there exists at least one path from C1 to C2.
Output Specification:
For each test case, print in one line two numbers: the number of different shortest paths between C1 and C2, and the maximum amount of rescue teams you can possibly gather. All the numbers in a line must be separated by exactly one space, and there is no extra space allowed at the end of a line.
Sample Input:
5 6 0 2
1 2 1 5 3
0 1 1
0 2 2
0 3 1
1 2 1
2 4 1
3 4 1
Sample Output:
2 4
AC代码:
#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
#define int long long //可能会超时
#define PII pair<int,int>
const int INF = 0x3f3f3f3f, mod = 998244353;
const int N = 505;
int mincost[N];
int g[N][N], vis[N], cnt[N], sum[N], a[N], n, m, s, d;
void dijk(int s) {
mincost[s] = 0;
sum[s] = a[s];
cnt[s] = 1;
while (1) {
int j = -1;
for (int i = 0;i < n;i++) {
if (!vis[i])
if (j == -1 || mincost[i] < mincost[j]) j = i;
}
if (j == -1) break;
vis[j] = 1;
for (int i = 0;i < n;i++) {
if (mincost[i] > mincost[j] + g[j][i]) {
mincost[i] = mincost[j] + g[j][i];
cnt[i] = cnt[j];
sum[i] = sum[j] + a[i];
}
else if (mincost[i] == mincost[j] + g[j][i]) {
cnt[i] += cnt[j];
if (sum[i] < sum[j] + a[i]) {
sum[i] = sum[j] + a[i];
}
}
}
}
}
signed main()
{
ios::sync_with_stdio, cin.tie(0), cout.tie(0);
memset(g, INF, sizeof g);
memset(mincost, INF, sizeof mincost);
cin >> n >> m >> s >> d;
for (int i = 0;i < n;i++) cin >> a[i];
while (m--) {
int a, b, c;
cin >> a >> b >> c;
g[a][b] = g[b][a] = c;
}
dijk(s);
cout << cnt[d] << ' ' << sum[d];
return 0;
}
3.
2024 睿抗机器人开发者大赛CAIP-编程技能赛-本科组(国赛)RC-u4 City 不 City
“City 不 City”因为一位外国友人保保熊直播旅游时总是用奇怪的腔调说“好 city,啊!”而走红中国社交网络,成为网络热梗。事实上,有一些叛逆的年轻人在旅行时会刻意避开网红打卡点,选择一些小众的特色地方小城镇,不追求 city,而喜欢说“好 country,啊”。
下面给定各个城镇的旅游热度和城镇间的旅行花销,请你为前来咨询的旅行者规划一条最经济的路线,并且尽可能避开热度很高的网红点。
输入格式:
输入第一行首先给出 4 个正整数:n 和 m(1<n≤103,1≤m≤5n),依次为城镇数量(于是城镇编号从 1 到 n)和城镇间的通路条数;s 和 t 依次为旅行者的出发地和目的地的城镇编号。
随后一行给出 n 个不超过 100 的正整数,依次为 n 个城镇的旅游热度。
再后面是 m 行,每行给出一条通路连接的两个城镇的编号、这条通路的最小花销(其数值为不超过 103 的正整数)。通路是双向的,题目保证任一对城镇间至多给出一条通路。
同一行的数字间均以空格分隔。
输出格式:
题目要求从 s 到 t 的最小花销路线;若这样的路线不唯一,则取途径城镇的最高旅游热度值最小的那条路线。
在一行中输出从 s 到 t 的最小花销、以及途经城镇的最高旅游热度值(若没有途经的城镇,则热度值为 0)。数值间以 1 个空格分隔,行首尾不得有多余空格。
若从 s 根本走不到 t,则在一行中输出 Impossible
。
输入样例 1:
8 14 7 8
100 20 30 10 50 80 100 100
7 1 1
7 2 2
7 3 1
7 4 2
1 2 1
1 5 2
2 5 1
3 4 1
3 5 3
3 6 2
4 6 1
5 6 1
5 8 1
6 8 2
输出样例 1:
4 50
样例解释:
从 7 到 8 的最短路径有 3 条,其中 2 条都经过城镇 1,于是对应的最高旅游热度值是城镇 1 的热度值 100。解路径为 7->2->5->8,途径城镇 2 和 5,对应的最高旅游热度值是城镇 5 的热度值 50。在最短路径长度相等的情况下,取热度值小的解,故输出的热度值为 50。
输入样例 2:
3 1 1 2
10 20 30
1 3 1
输出样例 2:
Impossible
AC代码:
#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
#define int long long //可能会超时
#define PII pair<int,int>
const int INF = 0x3f3f3f3f, mod = 998244353;
const int N = 1005;
int g[N][N], pre[N], a[N], mincost[N], sum[N], vis[N];
int n, m, s, d, ans;
stack<int>st;
vector<int>v;
void dijk(int s) {
mincost[s] = 0;
sum[s] = a[s];
while (1) {
int j = -1;
for (int i = 1;i <= n;i++) {
if (!vis[i])
if (j == -1 || mincost[i] < mincost[j]) j = i;
}
if (j == -1) break;
vis[j] = 1;
for (int i = 1;i <= n;i++) {
if (mincost[i] > mincost[j] + g[j][i]) {
mincost[i] = mincost[j] + g[j][i];
sum[i] = sum[j] + a[i];
pre[i] = j;
}
if (mincost[i] == mincost[j] + g[j][i]) {
if (sum[i] > sum[j] + a[i]) {
sum[i] = sum[j] + a[i];
pre[i] = j;
}
}
}
}
}
signed main()
{
ios::sync_with_stdio, cin.tie(0), cout.tie(0);
memset(g, INF, sizeof g);
memset(mincost, INF, sizeof mincost);
cin >> n >> m >> s >> d;
for (int i = 1;i <= n;i++) cin >> a[i];
while (m--) {
int a, b, c;
cin >> a >> b >> c;
g[a][b] = g[b][a] = c;
}
dijk(s);
if (mincost[d] < INF) {
cout << mincost[d] << ' ';
v.push_back(s);
while (d - s) {
st.push(d);
d = pre[d];
}
while (st.size()) {
v.push_back(st.top());
st.pop();
}
for (int i = 1;i < v.size() - 1;i++) ans = max(ans, a[v[i]]);
cout << ans;
}
else cout << "Impossible";
return 0;
}
4.PAT甲级 1030 Travel Plan
A traveler's map gives the distances between cities along the highways, together with the cost of each highway. Now you are supposed to write a program to help a traveler to decide the shortest path between his/her starting city and the destination. If such a shortest path is not unique, you are supposed to output the one with the minimum cost, which is guaranteed to be unique.
Input Specification:
Each input file contains one test case. Each case starts with a line containing 4 positive integers N, M, S, and D, where N (≤500) is the number of cities (and hence the cities are numbered from 0 to N−1); M is the number of highways; S and D are the starting and the destination cities, respectively. Then M lines follow, each provides the information of a highway, in the format:
City1 City2 Distance Cost
where the numbers are all integers no more than 500, and are separated by a space.
Output Specification:
For each test case, print in one line the cities along the shortest path from the starting point to the destination, followed by the total distance and the total cost of the path. The numbers must be separated by a space and there must be no extra space at the end of output.
Sample Input:
4 5 0 3
0 1 1 20
1 3 2 30
0 3 4 10
0 2 2 20
2 3 1 20
Sample Output:
0 2 3 3 40
AC代码:
#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
#define int long long //可能会超时
#define PII pair<int,int>
const int INF = 0x3f3f3f3f, mod = 998244353;
const int N = 505;
int g[N][N], mincost[N],sum[N] ,n, m, s, d, vis[N], pre[N], cost[N][N], mincost1[N];
stack<int>st;
void dijk(int s) {
mincost[s] = 0;
while (1) {
int j = -1;
for (int i = 0;i < n;i++) {
if (!vis[i])
if (j == -1 || mincost[i] < mincost[j]) j = i;
}
if (j == -1) break;
vis[j] = 1;
for (int i = 0;i < n;i++) {
if (mincost[i] > mincost[j] + g[j][i]) {
mincost[i] = mincost[j] + g[j][i];
sum[i] = sum[j] + cost[j][i];
pre[i] = j;
}
else if (mincost[i] == mincost[j] + g[j][i]) {
if (sum[i] > sum[j] + cost[j][i]) {
sum[i] = sum[j] + cost[j][i];
pre[i] = j;
}
}
}
}
}
signed main()
{
ios::sync_with_stdio, cin.tie(0), cout.tie(0);
memset(g, INF, sizeof g);
memset(cost, INF, sizeof cost);
memset(mincost, INF, sizeof mincost);
cin >> n >> m >> s >> d;
while (m--) {
int a, b, c, d;
cin >> a >> b >> c >> d;
g[a][b] = g[b][a] = c;
cost[a][b] = cost[b][a] = d;
}
dijk(s);
cout << s;
int dd=d;
while (dd - s) {
st.push(dd);
dd = pre[dd];
}
while (st.size()) {
cout << ' ' << st.top();
st.pop();
}
cout<<' '<<mincost[d]<<' '<<sum[d];
return 0;
}
5.
案例6-1.5 旅游规划
作者 陈越
单位 浙江大学
有了一张自驾旅游路线图,你会知道城市间的高速公路长度、以及该公路要收取的过路费。现在需要你写一个程序,帮助前来咨询的游客找一条出发地和目的地之间的最短路径。如果有若干条路径都是最短的,那么需要输出最便宜的一条路径。
输入格式:
输入说明:输入数据的第 1 行给出 4 个正整数 n、m、s、d,其中 n(2≤n≤500)是城市的个数,顺便假设城市的编号为 0~(n−1);m 是高速公路的条数;s 是出发地的城市编号;d 是目的地的城市编号。随后的 m 行中,每行给出一条高速公路的信息,分别是:城市 1、城市 2、高速公路长度、收费额,中间用空格分开,数字均为整数且不超过 500。输入保证解的存在。
输出格式:
在一行里输出路径的长度和收费总额,数字间以空格分隔,输出结尾不能有多余空格。
输入样例:
4 5 0 3
0 1 1 20
1 3 2 30
0 3 4 10
0 2 2 20
2 3 1 20
输出样例:
3 40
AC代码:
#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
#define int long long //可能会超时
#define PII pair<int,int>
const int INF = 0x3f3f3f3f, mod = 998244353;
const int N = 505;
int g[N][N], n, m, s, d, mincost[N], fee[N][N],sum[N];
int vis[N];
void dijk(int s) {
mincost[s] = 0;
while (1) {
int j = -1;
for (int i = 0;i < n;i++) {
if (!vis[i])
if (j == -1 || mincost[i] < mincost[j]) j = i;
}
if (j == -1) break;
vis[j] = 1;
for (int i = 0;i < n;i++) {
if (mincost[i] > mincost[j] + g[j][i]) {
mincost[i] = mincost[j] + g[j][i];
sum[i] = sum[j] + fee[j][i];
}
else if (mincost[i] == mincost[j] + g[j][i]) {
if (sum[i] > sum[j] + fee[j][i])
sum[i] = sum[j] + fee[j][i];
}
}
}
}
signed main()
{
ios::sync_with_stdio, cin.tie(0), cout.tie(0);
memset(g, INF, sizeof g);
memset(mincost, INF, sizeof mincost);
cin >> n >> m >> s >> d;
while (m--) {
int a, b, c, d;
cin >> a >> b >> c >> d;
g[a][b] = g[b][a] = c;
fee[a][b] = fee[b][a] = d;
}
dijk(s);
cout << mincost[d] << ' ' << sum[d];
return 0;
}
6.
L3-007 天梯地图
分数 30
作者 陈越
单位 浙江大学
本题要求你实现一个天梯赛专属在线地图,队员输入自己学校所在地和赛场地点后,该地图应该推荐两条路线:一条是最快到达路线;一条是最短距离的路线。题目保证对任意的查询请求,地图上都至少存在一条可达路线。
输入格式:
输入在第一行给出两个正整数N
(2 ≤ N
≤ 500)和M
,分别为地图中所有标记地点的个数和连接地点的道路条数。随后M
行,每行按如下格式给出一条道路的信息:
V1 V2 one-way length time
其中V1
和V2
是道路的两个端点的编号(从0到N
-1);如果该道路是从V1
到V2
的单行线,则one-way
为1,否则为0;length
是道路的长度;time
是通过该路所需要的时间。最后给出一对起点和终点的编号。
输出格式:
首先按下列格式输出最快到达的时间T
和用节点编号表示的路线:
Time = T: 起点 => 节点1 => ... => 终点
然后在下一行按下列格式输出最短距离D
和用节点编号表示的路线:
Distance = D: 起点 => 节点1 => ... => 终点
如果最快到达路线不唯一,则输出几条最快路线中最短的那条,题目保证这条路线是唯一的。而如果最短距离的路线不唯一,则输出途径节点数最少的那条,题目保证这条路线是唯一的。
如果这两条路线是完全一样的,则按下列格式输出:
Time = T; Distance = D: 起点 => 节点1 => ... => 终点
输入样例1:
10 15
0 1 0 1 1
8 0 0 1 1
4 8 1 1 1
5 4 0 2 3
5 9 1 1 4
0 6 0 1 1
7 3 1 1 2
8 3 1 1 2
2 5 0 2 2
2 1 1 1 1
1 5 0 1 3
1 4 0 1 1
9 7 1 1 3
3 1 0 2 5
6 3 1 2 1
5 3
输出样例1:
Time = 6: 5 => 4 => 8 => 3
Distance = 3: 5 => 1 => 3
输入样例2:
7 9
0 4 1 1 1
1 6 1 3 1
2 6 1 1 1
2 5 1 2 2
3 0 0 1 1
3 1 1 3 1
3 2 1 2 1
4 5 0 2 2
6 5 1 2 1
3 5
输出样例2:
Time = 3; Distance = 4: 3 => 2 => 5
AC代码:
#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
#define int long long //可能会超时
#define PII pair<int,int>
const int INF = 0x3f3f3f3f, mod = 998244353;
const int N = 505;
int n, m, g[N][N], gt[N][N], mincost[N], mintime[N];
int pret[N], pred[N];
int s, d;
int dis[N], cnt[N], vist[N];
int m1, m2;
stack<int>st, sd;
vector<int>ans1, ans2;
void dijkt(int s) {
mintime[s] = 0;
while (1) {
int j = -1;
for (int i = 0;i < n;i++) {
if (!vist[i])
if (j == -1 || mintime[i] < mintime[j]) j = i;
}
if (j == -1) break;
vist[j] = 1;
for (int i = 0;i < n;i++) {
if (mintime[i] > mintime[j] + gt[j][i]) {
mintime[i] = mintime[j] + gt[j][i];
pret[i] = j;
dis[i] = dis[j] + g[j][i];
}
else if (mintime[i] == mintime[j] + gt[j][i]) {
if (dis[i] > dis[j] + g[j][i]) {
dis[i] = dis[j] + g[j][i];
pret[i] = j;
}
}
}
}
}
void dijkd(int s) {
mincost[s] = 0;
while (1) {
int j = -1;
for (int i = 0;i < n;i++) {
if (!vist[i])
if (j == -1 || mincost[i] < mincost[j]) j = i;
}
if (j == -1) break;
vist[j] = 1;
for (int i = 0;i < n;i++) {
if (mincost[i] > mincost[j] + g[j][i]) {
mincost[i] = mincost[j] + g[j][i];
cnt[i] = cnt[j] + 1;
pred[i] = j;
}
else if (mincost[i] == mincost[j] + g[j][i]) {
if (cnt[i] > cnt[j] + 1) {
cnt[i] = cnt[j] + 1;
pred[i] = j;
}
}
}
}
}
signed main()
{
ios::sync_with_stdio, cin.tie(0), cout.tie(0);
memset(g, INF, sizeof g);
memset(gt, INF, sizeof gt);
memset(mincost, INF, sizeof mincost);
memset(mintime, INF, sizeof mintime);
cin >> n >> m;
while (m--) {
int v1, v2, one, length, time;
cin >> v1 >> v2 >> one >> length >> time;
if (one == 1) {
g[v1][v2] = length;
gt[v1][v2] = time;
}
else {
g[v1][v2] = g[v2][v1] = length;
gt[v1][v2] = gt[v2][v1] = time;
}
}
cin >> s >> d;
int s1 = s, d1 = d, s2 = s, d2 = d;
dijkt(s);
memset(vist, 0, sizeof vist);
m1 = mintime[d];
ans1.push_back(s);
while (s1 - d) {
st.push(d);
d = pret[d];
}
while (st.size()) {
ans1.push_back(st.top());
st.pop();
}
dijkd(s);
m2 = mincost[d1];
ans2.push_back(s);
while (s1 - d1) {
st.push(d1);
d1 = pred[d1];
}
while (st.size()) {
ans2.push_back(st.top());
st.pop();
}
if (ans1 == ans2) {
printf("Time = %d; Distance = %d: ", m1, m2);
cout << ans1[0];
for (int i = 1;i < ans1.size();i++) cout << " => " << ans1[i];
}
else {
printf("Time = %d: ", m1);
cout << ans1[0];
for (int i = 1;i < ans1.size();i++) cout << " => " << ans1[i];
puts("");
printf("Distance = %d: ", m2);
cout << ans2[0];
for (int i = 1;i < ans2.size();i++) cout << " => " << ans2[i];
}
return 0;
}
7.
L3-011 直捣黄龙
分数 30
作者 陈越
单位 浙江大学
本题是一部战争大片 —— 你需要从己方大本营出发,一路攻城略地杀到敌方大本营。首先时间就是生命,所以你必须选择合适的路径,以最快的速度占领敌方大本营。当这样的路径不唯一时,要求选择可以沿途解放最多城镇的路径。若这样的路径也不唯一,则选择可以有效杀伤最多敌军的路径。
输入格式:
输入第一行给出 2 个正整数 N(2 ≤ N ≤ 200,城镇总数)和 K(城镇间道路条数),以及己方大本营和敌方大本营的代号。随后 N-1 行,每行给出除了己方大本营外的一个城镇的代号和驻守的敌军数量,其间以空格分隔。再后面有 K 行,每行按格式城镇1 城镇2 距离
给出两个城镇之间道路的长度。这里设每个城镇(包括双方大本营)的代号是由 3 个大写英文字母组成的字符串。
输出格式:
按照题目要求找到最合适的进攻路径(题目保证速度最快、解放最多、杀伤最强的路径是唯一的),并在第一行按照格式己方大本营->城镇1->...->敌方大本营
输出。第二行顺序输出最快进攻路径的条数、最短进攻距离、歼敌总数,其间以 1 个空格分隔,行首尾不得有多余空格。
输入样例:
10 12 PAT DBY
DBY 100
PTA 20
PDS 90
PMS 40
TAP 50
ATP 200
LNN 80
LAO 30
LON 70
PAT PTA 10
PAT PMS 10
PAT ATP 20
PAT LNN 10
LNN LAO 10
LAO LON 10
LON DBY 10
PMS TAP 10
TAP DBY 10
DBY PDS 10
PDS PTA 10
DBY ATP 10
输出样例:
PAT->PTA->PDS->DBY
3 30 210
AC代码:
#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
#define int long long //可能会超时
#define PII pair<int,int>
const int INF = 0x3f3f3f3f, mod = 998244353;
const int N = 205;
unordered_map<string, int>mp;
unordered_map<int, string>mp1;
int n, m, st, ed;
int cnt[N], mincost[N], sum[N],a[N];
int g[N][N],vis[N],cntcity[N];
int pre[N];
string s1, s2;
stack<int>s;
void dijk(int s) {
mincost[s] = 0;
cnt[s] = 1;
sum[s] = a[s];
while (1) {
int j = -1;
for (int i = 0;i < n;i++) {
if (!vis[i])
if (j == -1 || mincost[i] < mincost[j]) j = i;
}
if (j == -1) break;
vis[j] = 1;
for (int i = 0;i < n;i++) {
if (mincost[i] > mincost[j] + g[j][i]) {
cnt[i] = cnt[j];
mincost[i] = mincost[j] + g[j][i];
sum[i] = sum[j] + a[i];
pre[i] = j;
cntcity[i] = cntcity[j] + 1;
}
else if (mincost[i] == mincost[j] + g[j][i]) {
cnt[i] += cnt[j];
if (cntcity[i] < cntcity[j] + 1) {
cntcity[i] = cntcity[j] + 1;
sum[i] = sum[j] + a[i];
pre[i] = j;
}
else if (cntcity[i] == cntcity[j] + 1) {
if (sum[i] < sum[j] + a[i]) {
sum[i] = sum[j] + a[i];
pre[i] = j;
}
}
}
}
}
}
signed main()
{
ios::sync_with_stdio, cin.tie(0), cout.tie(0);
memset(mincost, INF, sizeof mincost);
memset(g, INF, sizeof g);
cin >> n >> m>>s1>>s2;
mp[s1] = 0;
mp1[0] = s1;
for (int i = 1;i < n;i++) {
string s;
cin >> s >> a[i];
mp[s] = i;
mp1[i] = s;
}
while (m--) {
string a, b;
int dis;
cin >> a >> b >> dis;
g[mp[a]][mp[b]] = g[mp[b]][mp[a]] = dis;
}
dijk(0);
int d = mp[s2];
int temp = mp[s2];
cout << mp1[0];
while (d) {
s.push(d);
d = pre[d];
}
while (s.size()) {
cout << "->" << mp1[s.top()];
s.pop();
}
puts("");
cout <<cnt[temp]<<' '<<mincost[temp]<<' '<<sum[temp];
return 0;
}
8.
L3-005 垃圾箱分布
分数 30
作者 陈越
单位 浙江大学
大家倒垃圾的时候,都希望垃圾箱距离自己比较近,但是谁都不愿意守着垃圾箱住。所以垃圾箱的位置必须选在到所有居民点的最短距离最长的地方,同时还要保证每个居民点都在距离它一个不太远的范围内。
现给定一个居民区的地图,以及若干垃圾箱的候选地点,请你推荐最合适的地点。如果解不唯一,则输出到所有居民点的平均距离最短的那个解。如果这样的解还是不唯一,则输出编号最小的地点。
输入格式:
输入第一行给出4个正整数:N(≤103)是居民点的个数;M(≤10)是垃圾箱候选地点的个数;K(≤104)是居民点和垃圾箱候选地点之间的道路的条数;DS是居民点与垃圾箱之间不能超过的最大距离。所有的居民点从1到N编号,所有的垃圾箱候选地点从G1到GM编号。
随后K行,每行按下列格式描述一条道路:
P1 P2 Dist
其中P1
和P2
是道路两端点的编号,端点可以是居民点,也可以是垃圾箱候选点。Dist
是道路的长度,是一个正整数。
输出格式:
首先在第一行输出最佳候选地点的编号。然后在第二行输出该地点到所有居民点的最小距离和平均距离。数字间以空格分隔,保留小数点后1位。如果解不存在,则输出No Solution
。
输入样例1:
4 3 11 5
1 2 2
1 4 2
1 G1 4
1 G2 3
2 3 2
2 G2 1
3 4 2
3 G3 2
4 G1 3
G2 G1 1
G3 G2 2
输出样例1:
G1
2.0 3.3
输入样例2:
2 1 2 10
1 G1 9
2 G1 20
输出样例2:
No Solution
AC代码:
#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
#define int long long //可能会超时
#define PII pair<int,int>
const int INF = 0x3f3f3f3f, mod = 998244353;
const int N = 1e3 + 15;
int g[N][N], mincost[N], vis[N];
int n, m, k, d;
struct node {
int dis, id;
double avg;
};
bool cmp(node a, node b) {
if (a.dis - b.dis) return a.dis > b.dis;
if (a.avg - b.avg) return a.avg < b.avg;
return a.id < b.id;
}
double get(double a) { //两位小数四舍五入成一位小数
int n = round(a * 10);
return n / 10.0;
}
vector<node>v;
void dijk(int s) {
mincost[s] = 0;
while (1) {
int j = -1;
for (int i = 1;i <= n + m;i++) {
if (!vis[i])
if (j == -1 || mincost[i] < mincost[j]) j = i;
}
if (j == -1) break;
vis[j] = 1;
for (int i = 1;i <= n + m;i++) {
mincost[i] = min(mincost[i], mincost[j] + g[j][i]);
}
}
}
signed main()
{
ios::sync_with_stdio, cin.tie(0), cout.tie(0);
cin >> n >> m >> k >> d;
memset(g, INF, sizeof g);
while (k--) {
string a, b;
int x, y;
int d;
cin >> a >> b >> d;
if (a[0] == 'G') {
if (a.size() == 3) x = n + 10;
else x = (a[1] - '0') + n;
}
else {
x = stoi(a);
}
if (b[0] == 'G') {
if (b.size() == 3) y = n + 10;
else y = (b[1] - '0') + n;
}
else {
y = stoi(b);
}
g[x][y] = g[y][x] = d;
}
for (int i = n + 1;i <= n + m;i++) {
int ans = INF, sum = 0, min_ans = -1;
memset(mincost, INF, sizeof mincost);
memset(vis, 0, sizeof vis);
dijk(i);
for (int j = 1;j <= n;j++) {
ans = min(ans, mincost[j]);
min_ans = max(min_ans, mincost[j]);
sum += mincost[j];
}
double avg = sum * 1.0 / n;
if (ans - INF && sum - INF && min_ans <= d) {
v.push_back({ ans,i,avg });
}
}
if (v.size()) {
sort(v.begin(), v.end(), cmp);
cout << "G" << v[0].id - n << endl;
double dis = v[0].dis;
printf("%.1f %.1f", dis, get(v[0].avg));
}
else cout << "No Solution";
return 0;
}