我的Data Structures and Algorithms代码仓:https://github.com/617076674/Data-Structures-and-Algorithms
原题链接:https://pintia.cn/problem-sets/16/problems/673
题目描述:
知识点:SPFA算法、深度优先遍历(回溯)
思路:SPFA算法+深度优先遍历(回溯)
定义一个结构体situation来保存坐标,题目表明鳄鱼数量不超过100条,再加上原点坐标(0, 0),因此我们可以用一个大小为101的数组situations来存储各个situation的信息。一个situations数组中的索引就相当于一个situation,也相当于图中的一个点。
这样这个问题就成了从点0开始,求能逃出去的最短路径。注意,这里路径的定义——“The length of a path is the number of jumps that James has to make.”,这里的路径定义为跳跃步数。
为避免对浮点数的处理,我们的图graph不保存路径长度,而保存路径长度的平方。
如果从中心岛一步就能逃出该区域,直接输出“1”即可,这种情况需要特殊处理。
用SPFA算法求得(0, 0)到其余各点的最短距离后,就可以从遍历所有能逃出去的鳄鱼的位置,用dfs求出满足题意的最短路径。
期望时间复杂度是O(kM),其中k是一个常数,在很多情况下k不超过2,M是图中的边数,可见这个算法异常高效,并且经常性地优于堆优化的Dijkstra算法。空间复杂度是O(N ^ 2)。
C++代码:
#include<iostream>
#include<vector>
#include<queue>
#include<set>
using namespace std;
struct situation {
int x, y;
};
int N, D, INF = 1000000000;
situation situations[101];
int graph[101][101]; //无向图graph[i][j]代表situations[i]与situations[j]之间存在的路径长度的平方
int d[101];
int countInq[101];
bool inq[101];
set<int> pre[101];
vector<int> tempPath;
vector<int> path;
int minSteps = INF; //最少跳跃步数
int minFirstJump = INF; //第一步跳的距离最短
int calculateDistance(situation s1, situation s2);
bool canEscape(situation s); //从s位置能否逃出
bool spfa(int s);
void dfs(int nowVisit);
int main() {
situations[0].x = situations[0].y = 0; //situations[0]是原点,坐标为(0, 0)
scanf("%d %d", &N, &D);
for(int i = 1; i <= N; i++) {
scanf("%d %d", &situations[i].x, &situations[i].y);
}
if((D + 7.5) * (D + 7.5) >= 50 * 50) { //如果一步就能跳出去
printf("1\n");
return 0;
}
for(int i = 0; i < N + 1; i++) {
for(int j = i + 1; j < N + 1; j++) {
graph[i][j] = graph[j][i] = INF;
}
}
for(int i = 1; i < N + 1; i++) {
int distance = calculateDistance(situations[0], situations[i]);
if(distance <= (D + 7.5) * (D + 7.5)) {
graph[0][i] = graph[i][0] = distance;
}
}
for(int i = 1; i < N + 1; i++) {
for(int j = i + 1; j < N + 1; j++) {
int distance = calculateDistance(situations[i], situations[j]);
if(distance <= D * D) {
graph[i][j] = graph[j][i] = distance;
}
}
}
spfa(0);
for(int i = 1; i < N + 1; i++) {
if(!canEscape(situations[i])) {
continue;
}
dfs(i);
}
printf("%d\n", path.size());
for(int i = path.size() - 2; i >= 0; i--) {
printf("%d %d\n", situations[path[i]].x, situations[path[i]].y);
}
return 0;
}
int calculateDistance(situation s1, situation s2) {
return (s1.x - s2.x) * (s1.x - s2.x) + (s1.y - s2.y) * (s1.y - s2.y);
}
bool canEscape(situation s) {
int len1, len2;
if(s.y >= 0) {
len1 = 50 - s.y;
} else {
len1 = s.y + 50;
}
if(s.x >= 0) {
len2 = 50 - s.x;
} else {
len2 = s.x + 50;
}
if(min(len1, len2) <= D) {
return true;
} else {
return false;
}
}
bool spfa(int s) {
fill(d, d + N + 1, INF);
fill(countInq, countInq + N + 1, 0);
fill(inq, inq + N + 1, false);
d[s] = 0;
queue<int> q;
q.push(s);
countInq[s]++;
inq[s] = true;
while(!q.empty()) {
int now = q.front();
q.pop();
inq[now] = false;
for(int i = 0; i < N + 1; i++) {
if(graph[now][i] < INF) {
if(d[now] + 1 < d[i]) {
d[i] = d[now] + 1;
pre[i].clear();
pre[i].insert(now);
if(!inq[i]) {
q.push(i);
countInq[i]++;
inq[i] = true;
if(countInq[i] > N) {
return false;
}
}
} else if(d[now] + 1 == d[i]) {
pre[i].insert(now);
if(!inq[i]) {
q.push(i);
countInq[i]++;
inq[i] = true;
if(countInq[i] > N) {
return false;
}
}
}
}
}
}
return true;
}
void dfs(int nowVisit) {
tempPath.push_back(nowVisit);
if(nowVisit == 0) {
if(tempPath.size() < minSteps) {
path = tempPath;
minSteps = tempPath.size();
minFirstJump = graph[0][tempPath[tempPath.size() - 2]];
} else if(tempPath.size() == minSteps && graph[0][tempPath[tempPath.size() - 2]] < minFirstJump) {
path = tempPath;
minFirstJump = graph[0][tempPath[tempPath.size() - 2]];
}
tempPath.pop_back();
return;
}
for(set<int>::iterator it = pre[nowVisit].begin(); it != pre[nowVisit].end(); it++) {
dfs(*it);
}
tempPath.pop_back();
}
C++解题报告: