A*算法需要预估距离来向最短的路径进行引导,使得更容易向最短路方向前进。所以其实dijkstra算法可以看作预估距离为0的A*算法。由于A*算法需要预估距离,A*算法常用在二维矩阵图上或者可以预估距离的图
//double f(int a, int b, int c, int d) {//欧式距离
// return sqrt(pow(a - c, 2) + pow(b - d, 2));
//}
//int f(int a, int b, int c, int d) {//对角线距离,用在既可以走直线,也可以走对角线的图
// return max(abs(a - c), abs(b - d));
//}
int f(int a, int b, int c, int d) {//曼哈顿距离
return abs(a - c) + abs(b - d);
}
//grid由01组成,其中0表示障碍,1表示通路,求从起点到终点的最短距离
int mov[5] = { -1,0,1,0,-1 };
int mindistance(vector<vector<int>>grid, int startx, int starty, int targetx, int targety) {
if (grid[startx][starty] == 0 || grid[targetx][targety] == 0)
return -1;
int n = grid.size();
int m = grid[0].size();
vector<vector<int>>distanc(n, vector<int>(m, INT_MAX));
vector<vector<bool>>visited(n, vector<bool>(m, false));
distanc[startx][starty] = 1;
auto cmp = [](vector<int>a, vector<int>b){ return a[2] > b[2]; };
priority_queue<vector<int>, vector<vector<int>>, decltype(cmp)>heap(cmp);
heap.push({ startx,starty,1+f(startx,starty,targetx,targety)});
while (!heap.empty()) {
vector<int>rem = heap.top();
heap.pop();
int x = rem[0];
int y = rem[1];
int w = rem[2];
if (visited[x][y]) continue;
visited[x][y] = true;
if (x == targetx && y == targety) return w;
for (int i = 0; i < 5; i++) {
int nx = x + mov[i];
int ny = y + mov[i + 1];
if (nx >= 0 && nx < n && ny >= 0 && ny < m && !visited[nx][ny]&&grid[nx][ny]==1&&distanc[x][y]+1<distanc[nx][ny]) {
distanc[nx][ny] = distanc[x][y] + 1;
heap.push({ nx,ny,distanc[nx][ny]+f(nx,ny,targetx,targety) });
}
}
}
return -1;
}
Floyd算法
只要是最短路问题,图中就不能有负环
通过枚举所有的点当作跳板点修改所有距离
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StreamTokenizer;
public class Main {
public static int MAXN = 101;
public static int MAXM = 10001;
public static int[] path = new int[MAXM];
public static int[][] distance = new int[MAXN][MAXN];
public static int n, m, ans;
// 初始时设置任意两点之间的最短距离为无穷大,表示任何路不存在
public static void build() {
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
distance[i][j] = Integer.MAX_VALUE;
}
}
}
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StreamTokenizer in = new StreamTokenizer(br);
PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
while (in.nextToken() != StreamTokenizer.TT_EOF) {
n = (int) in.nval;
in.nextToken();
m = (int) in.nval;
for (int i = 0; i < m; i++) {
in.nextToken();
path[i] = (int) in.nval - 1;
}
// 这道题给的图是邻接矩阵的形式
// 任意两点之间的边权都会给定
// 所以显得distance初始化不太必要
// 但是一般情况下,distance初始化一定要做
build();
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
in.nextToken();
distance[i][j] = (int) in.nval;
}
}
floyd();
ans = 0;
for (int i = 1; i < m; i++) {
ans += distance[path[i - 1]][path[i]];
}
out.println(ans);
}
out.flush();
out.close();
br.close();
}
public static void floyd() {
// O(N^3)的过程
// 枚举每个跳板
// 注意,跳板要最先枚举!跳板要最先枚举!跳板要最先枚举!
for (int bridge = 0; bridge < n; bridge++) { // 跳板
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
// i -> .....bridge .... -> j
// distance[i][j]能不能缩短
// distance[i][j] = min ( distance[i][j] , distance[i][bridge] + distance[bridge][j])
if (distance[i][bridge] != Integer.MAX_VALUE
&& distance[bridge][j] != Integer.MAX_VALUE
&& distance[i][j] > distance[i][bridge] + distance[bridge][j]) {
distance[i][j] = distance[i][bridge] + distance[bridge][j];
}
}
}
}
}
}
Bellman-Ford
class Solution {
public:
int findCheapestPrice(int n, vector<vector<int>>& flights, int src, int dst,
int k) {
vector<int> distance(n, INT_MAX);
distance[src] = 0;
for (int i = 0; i <= k; i++) {
vector<int> next = distance;
for (auto edge : flights) {
if (distance[edge[0]] != INT_MAX) {
next[edge[1]] =
min(next[edge[1]], distance[edge[0]] + edge[2]);
}
}
distance = next;
}
return distance[dst] == INT_MAX ? -1 : distance[dst];
}
};
此题中转的次数就可以看作松弛的轮数。但有所不同的是松弛一轮所改变的边在飞机中转的过程中可能不止中转一次,所以松弛的过程中需要创建next数组,对比原数组查看可以松弛哪些边
#include <iostream>
#include <climits>
#include <vector>
using namespace std;
const int maxn = 2001;
const int maxm = 6001;
const int maxq = 4000001;
int head[maxn];
int nex[maxm];
int to[maxm];
int value[maxm];
int cnt;
int distanc[maxn];
int cntnode[maxn];
int queue[maxq];
int l, r;
bool vistied[maxn];
void build(int n) {
cnt = 1;
l = 0, r = 0;
for (int i = 0; i <= n; i++) {
head[i] = 0;
distanc[i] = INT_MAX;
vistied[i] = false;
cntnode[i] = 0;
}
}
void insert(int u, int v, int w) {
nex[cnt] = head[u];
to[cnt] = v;
value[cnt] = w;
head[u] = cnt++;
}
string spfa(int n) {
distanc[1] = 0;
cntnode[1]++;
queue[r++] = 1;
vistied[1] = true;
while (l < r) {
int u = queue[l++];
vistied[u] = false;
for (int edge = head[u]; edge > 0; edge = nex[edge]) {
int v = to[edge];
int w = value[edge];
if (distanc[u] + w < distanc[v]) {
distanc[v] = distanc[u] + w;
if (!vistied[v]) {
queue[r++] = v;
vistied[v] = true;
//这一句就是为了判断是否有负环定制的,除去这一句就是spfa优化spellman
if ((cntnode[v]++) >= n) return "YES";//当松弛的轮数超过节点数时有环
}
}
}
}
return "NO";
}
int main() {
int t;
cin >> t;
while (t--) {
int n, m;
cin >> n >> m;
build(n);
while (m--) {
int a, b, c;
cin >> a >> b >> c;
if (c >= 0) {
insert(a, b, c);
insert(b, a, c);
}
else insert(a, b, c);
}
cout << spfa(n) << endl;
}
return 0;
}