A*算法--Floyd算法--Bellman Ford算法--SPFA算法

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

k站中转最便宜的航班

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;
}

  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值