注意, 本题解默认观看者理解dijkstra、floyd算法, 熟悉图的存储方式
很多游戏都有打怪升级的环节,玩家需要打败一系列怪兽去赢取成就和徽章。这里我们考虑一种简单的打怪升级游戏,游戏规则是,给定有 N 个堡垒的地图,堡垒之间有道路相连,每条道路上有一只怪兽把守。怪兽本身有能量,手里的武器有价值。打败怪兽需要的能量等于怪兽本身的能量,而怪兽一旦被打败,武器就归玩家所有 —— 当然缴获的武器价值越高,玩家就越开心。
你的任务有两件:
帮助玩家确定一个最合算的空降位置,即空降到地图中的某个堡垒,使得玩家从这个空降点出发,到攻下最难攻克(即耗费能量最多)的那个堡垒所需要的能量最小;
从这个空降点出发,帮助玩家找到攻克任意一个其想要攻克的堡垒的最省能量的路径。如果这种路径不唯一,则选择沿途缴获武器总价值最高的解,题目保证这种解是唯一的。
输入格式
输入第一行给出两个正整数 N (≤1000) 和 M,其中 N 是堡垒总数,M 是怪兽总数。为简单起见,我们将堡垒从 1 到 N 编号。随后 M 行,第 i 行给出了第 i 只怪兽的信息,格式如下:
B1 B2 怪兽能量 武器价值
其中 B1 和 B2 是怪兽把守的道路两端的堡垒编号。题目保证每对堡垒之间只有一只怪兽把守,并且 怪兽能量 和 武器价值 都是不超过 100 的正整数。
再后面是一个正整数 K(≤N)和玩家想要攻克的 K 个目标堡垒的编号。
输出格式
首先在一行中输出玩家空降的堡垒编号 B0。如果有多种可能,则输出编号最小的那个。
随后依次为玩家想要攻克的每个堡垒 B 推荐最省能量的攻克路径,并列出需要耗费的能量值和沿途缴获武器的总价值。注意如果最省力的路径不唯一,则选择沿途缴获武器总价值最高的解。格式为:
B0->途经堡垒1->…->B
总耗费能量 武器总价值
输入样例
6 12
1 2 10 5
2 3 16 20
3 1 4 2
2 4 20 22
4 5 2 2
5 3 12 6
4 6 8 5
6 5 10 5
6 1 20 25
1 5 8 5
2 5 2 1
2 6 8 5
4
2 3 6 5
输出样例
5
5->2
2 1
5->1->3
12 7
5->4->6
10 7
5
0 0
给定有 N 个堡垒的地图,堡垒之间有道路相连
解释:给定n个顶点的无向图, 且保证为连通图
每条道路上有一只怪兽把守。怪兽本身有能量,手里的武器有价值。打败怪兽需要的能量等于怪兽本身的能量
解释: 图中无重边, 每条边有两个权值, 分别为能量(距离) 和 价值(次权)
要求一
帮助玩家确定一个最合算的空降位置,即空降到地图中的某个堡垒,使得玩家从这个空降点出发,到攻下最难攻克(即耗费能量最多)的那个堡垒所需要的能量最小;
解释: 要分别求出图中每个顶点为起点的最短路径信息, 取最短路径信息中最大值最小的顶点为空降位置.要求一中只涉及路径(能量), 并没有涉及另一个权
要求二
从这个空降点出发,帮助玩家找到攻克任意一个其想要攻克的堡垒的最省能量的路径。如果这种路径不唯一,则选择沿途缴获武器总价值最高的解,题目保证这种解是唯一的。
解释: 以空降点为其它获取它的双权最短路径, 其中能量为主权, 只有能量相等时才会比较武器价值.
并且要记录路径, 后续输出.
对于要求一, 是一个多源单权最短路径问题, 使用floyd算法尤为方便.
用一个邻接矩阵存储图的能量信息, 对其使用floyd, 再在结果中找到空降点.
int g[N][N];
void floyd()
{
for(int i=1; i<=n; i++)
for(int x=1; x<=n; x++)
for(int y=x+1; y<=n; y++)
g[x][y] = g[y][x] = min(g[x][y], g[x][i]+g[i][y]);
}
//找出空降点
int Find()
{
int res, minE = INF;
for(int i=1; i<=n; i++)
{
int maxE = 0;
for(int j=1; j<=n; j++)
if(g[i][j] > maxE && i!=j)
maxE = g[i][j];
if(maxE < minE)
{
res = i;
minE = maxE;
}
}
return res;
}
对于要求二, 是一个单源双权最短路径问题, 使用dijkstra算法较为方便. 因为是dijkstra算法并且有双权, 所以这里用前向星来保存图的信息
//we为能量权, wv为价值权
int h[N], we[N], wv[N], e[N], ne[N], idx;
在dijkstra算法过程中我们不仅要维护最短距离/能量(dist)、状态数组vis、路径数组path, 还要维护最大价值value, 以确保最短距离的正确性
int dist[N];
int value[N];
bool vis[N];
int path[N];
为了方便, 使用了朴素版dijkstra
void dijkstra(int bg)
{
memset(dist, 0x3f, sizeof dist);
dist[bg] = 0;
for(int i=1; i<=n; i++)
{
int t = -1;
for(int j=1; j<=n; j++)
if(!vis[j] && (t==-1 || dist[j] < dist[t]))
t = j;
for(int j=h[t]; j!=-1; j=ne[j])
{
int dest = e[j];
//有两种情况要进行更新:路径较之前的小 或路径相同但价值较之前的大
if((dist[dest] > dist[t] + we[j]) ||
(dist[dest] == dist[t] + we[j] && value[dest] < value[t] + wv[j]))
{
dist[dest] = dist[t] + we[j];
value[dest] = value[t] + wv[j];
path[dest] = t;
}
}
vis[t] = true;
}
}
最后递归打印路径
void PrintPath(int bg, int tar)
{
if(bg == tar)
{
cout << bg;
return;
}
else PrintPath(bg, path[tar]);
cout << "->" << tar;
}
//
// Created by trudbot on 2022/7/8.
//
#include
#define ll long long
#define pii pair
#define INF 0x3f3f3f3f
using namespace std;
#define N 1010
int n, m;
int g[N][N];
int h[N], we[N], wv[N], e[N], ne[N], idx;
int dist[N];
int value[N];
bool vis[N];
int path[N];
void add(int x, int y, int z, int v)
{
wv[idx] = v, we[idx] = z, e[idx] = y, ne[idx] = h[x], h[x] = idx++;
}
void floyd()
{
for(int i=1; i<=n; i++)
for(int x=1; x<=n; x++)
for(int y=x+1; y<=n; y++)
g[x][y] = g[y][x] = min(g[x][y], g[x][i]+g[i][y]);
}
int Find()
{
int res, minE = INF;
for(int i=1; i<=n; i++)
{
int maxE = 0;
for(int j=1; j<=n; j++)
if(g[i][j] > maxE && i!=j)
maxE = g[i][j];
if(maxE < minE)
{
res = i;
minE = maxE;
}
}
return res;
}
void dijkstra(int bg)
{
memset(dist, 0x3f, sizeof dist);
dist[bg] = 0;
for(int i=1; i<=n; i++)
{
int t = -1;
for(int j=1; j<=n; j++)
if(!vis[j] && (t==-1 || dist[j] < dist[t]))
t = j;
for(int j=h[t]; j!=-1; j=ne[j])
{
int dest = e[j];
if((dist[dest] > dist[t] + we[j]) ||
(dist[dest] == dist[t] + we[j] && value[dest] < value[t] + wv[j]))
{
dist[dest] = dist[t] + we[j];
value[dest] = value[t] + wv[j];
path[dest] = t;
}
}
vis[t] = true;
}
}
void PrintPath(int bg, int tar)
{
if(bg == tar)
{
cout << bg;
return;
}
else PrintPath(bg, path[tar]);
cout << "->" << tar;
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
memset(h, -1, sizeof h);
memset(g, 0x3f, sizeof g);
cin >> n >> m;
int x, y, z, v;
while (m--)
{
cin >> x >> y >> z >> v;
g[x][y] = g[y][x] = z;
add(x, y, z, v);
add(y, x, z, v);
}
floyd();
int bg = Find();
cout << bg << endl;
dijkstra(bg);
int k; cin >> k;
int tar;
while(k--)
{
cin >> tar;
PrintPath(bg, tar);
cout << endl;
cout << dist[tar] << " " << value[tar] << endl;
}
return 0;
}