Dijkstra && 分层图最短路

Dijkstra算法:给定一个源点,求解从源点到每个点的最短的路径长度。

适用范围:有向图,边的权值没有负数

普通堆实现dijkstra:最普遍,最常用;时间复杂度O(m*logm),m为边数

 1.distanc[i]表示从源点到i点的最短距离,visit[i]表示节点i是否从小根堆里弹出过

 2.准备好小根堆,小根堆存放记录:(x,x到源点的距离),小根堆根据距离组织

 3.令distancs[源点]=0,(源点,0)进入堆

 4.从小根堆里弹出:(u,w)

   如果visit[u]=true,不做处理,重复步骤4;

   如果visit[u]=false,令visit[u]=true,考察u的每一条边(v,m)

         如果visit[v]=false并且distance[u]+m<distance[v],令distance[v]=distance[u]+m,加入小根堆;重复步骤4

   5.直至小根堆为空,distance记录了源点到各点的最短距离    

网络延迟时间----模板

class Solution {
public:
    static const int maxnum = 100;
    int distance[maxnum];
    bool visit[maxnum];
    vector<vector<vector<int>>> pragh;//动态建图
    void build(int n) {
        for (int i = 0; i <= n; i++) {
            pragh.push_back(vector<vector<int>>());
            distance[i] = INT_MAX;
            visit[i] = false;
        }
    }
    int networkDelayTime(vector<vector<int>>& times, int n, int k) {
        build(n);
        for (auto edge : times) {
            pragh[edge[0]].push_back({edge[1], edge[2]});
        }
        distance[k] = 0;
        auto cmp = [](vector<int> a, vector<int> b) { return a[1] > b[1]; };
        priority_queue<vector<int>, vector<vector<int>>, decltype(cmp)> heap(
            cmp);
        heap.push({k, 0});
        while (!heap.empty()) {
            vector<int> re = heap.top();
            heap.pop();
            int u = re[0];
            int w = re[1];
            if (visit[u])//弹出过就跳过
                continue;
            visit[u] = true;
            for (auto edge : pragh[u]) {
                int v = edge[0];
                int m = edge[1];
                if (!visit[v] && distance[u] + m < distance[v]) {//
                    distance[v] = distance[u] + m;
                    heap.push({v, distance[v]});//加入更小修改之后的边
                }
            }
        }
        int ans = INT_MIN;
        for (int i = 1; i <= n; i++) {
            if (distance[i] == INT_MAX)
                return -1;
            ans = max(ans, distance[i]);
        }
        return ans;
    }
};

 反向索引堆实现Dijkstra算法:时间复杂度为(m*logn),n为节点数,m为边数

 单源最短路径---模板

#include <iostream>
#include <vector>
#include <algorithm>
#include <climits>
using namespace std;
const int maxn = 100001;
const int maxm = 200001;
int head[maxn];
int nex[maxm];
int to[maxm];
int value[maxm];
int cnt;
int heap[maxn];
int where[maxn];
int heapsize;
int distanc[maxn];
int n, m, s;
void build() {//建图
	for (int i = 1; i <= n; i++) {
		head[i] = 0;
		distanc[i] = INT_MAX;
		where[i] = -1;
	}
	cnt = 1;
}
void insert(int u, int v, int w) {//插入图
	nex[cnt] = head[u];
	to[cnt] = v;
	value[cnt] = w;
	head[u] = cnt++;
}
void swap(int i, int j) {//交换堆
	int rem = heap[i];
	heap[i] = heap[j];
	heap[j] = rem;
	where[heap[i]] = i;
	where[heap[j]] = j;
}
void heapify(int i) {//向下调整堆
	int l = 2 * i + 1;
	while (l < heapsize) {
		int best = l + 1 < heapsize && distanc[heap[l + 1]] < distanc[heap[l]] ? l + 1 : l;
	    best = distanc[heap[best]] < distanc[heap[i]] ? best : i;
		if (best == i) break;
		swap(i, best);
		i = best;
		l = 2 * i + 1;
	}
	
}
 int pop() {//弹出节点
	int ans = heap[0];
	swap(0, --heapsize);
	heapify(0);
	where[ans] = -2;
	return ans;
}
void heapinsert(int i) {//堆插入
	while (distanc[heap[i]] < distanc[heap[(i-1)/2]]) {
		swap(i, (i - 1) / 2);
		i = (i - 1) / 2;
	}
}
void addoringorne(int v, int w) {
	if (where[v] == -1) {
		heap[heapsize] = v;
		where[v] = heapsize++;
		distanc[v] = w;
		heapinsert(where[v]);
	}
	else if (where[v] >= 0) {
		distanc[v] = min(distanc[v], w);
		heapinsert(where[v]);
	}
}
int main() {
	cin >> n >> m >> s;
	build();
	while (m--) {
		int a, b, c;
		cin >> a >> b >> c;
		insert(a, b, c);
	}
	distanc[s] = 0;
	addoringorne(s, 0);
	while (heapsize > 0) {
		int v = pop();
		for (int i = head[v]; i != 0; i = nex[i]) {
			addoringorne(to[i], distanc[v] + value[i]);
		}
	}
	for (int i = 1; i <= n; i++)
		cout << distanc[i] << " ";
	return 0;
}

最小体能消耗

class Solution {
public:
    int move[5] = {-1, 0, 1, 0, -1};
    bool visited[101][101] = {0};
    int minimumEffortPath(vector<vector<int>>& heights) {
        ios::sync_with_stdio(false);
        cin.tie(0), cout.tie(0);
        int n = heights.size();
        int m = heights[0].size();
        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({0, 0, 0});
        while (!heap.empty()) {
            vector<int> rem = heap.top();
            heap.pop();
            int x = rem[0];
            int y = rem[1];
            int w = rem[2];
            if (x == n - 1 && y == m - 1)
                return w;
            visited[x][y] = true;
            for (int i = 0; i < 4; i++) {
                int nx = x + move[i];
                int ny = y + move[i + 1];
                if (nx >= 0 && nx < n && ny >= 0 && ny < m &&
                    visited[nx][ny] == false) {
                    int r = abs(heights[x][y] - heights[nx][ny]);
                    int nw = max(w, r);
                    heap.push({nx, ny, nw});
                }
            }
        }
        return -1;
    }
};

 

获得钥匙的最短距离 

class Solution {
public:
    static const int maxn = 31;
    static const int maxm = 31;
    static const int maxk = 6;
    int move[5] = {-1, 0, 1, 0, -1};
    bool visited[maxn][maxm][1 << maxk];
    int queue[maxm * maxn * (1 << maxk)][3];
    int l, r, n, m, key;
    void build(vector<string>& grid) {
        n = grid.size();
        m = grid[0].size();
        key = 0;
        l = 0, r = 0;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (grid[i][j] == '@') {
                    queue[r][0] = i;
                    queue[r][1] = j;
                    queue[r++][2] = 0;
                }
                if (grid[i][j] >= 'a' && grid[i][j] <= 'f') {
                    key |= (1 << (grid[i][j] - 'a'));
                }
            }
        }
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                for (int k = 0; k <= key; k++)
                    visited[i][j][k] = false;
            }
        }
    }
    int shortestPathAllKeys(vector<string>& grid) {
        build(grid);
        int level = 1;
        while (l < r) {
            int size = r - l;
            while (size--) {
                int x = queue[l][0];
                int y = queue[l][1];
                int ke = queue[l++][2];
                for (int i = 0; i < 4; i++) {
                    int nx = x + move[i];
                    int ny = y + move[i + 1];
                    int nke = ke;
                    if (nx >= 0 && ny >= 0 && nx < n && ny < m &&
                        grid[nx][ny] != '#') {
                        if (grid[nx][ny] >= 'A' && grid[nx][ny] <= 'F' &&(((nke>>(grid[nx][ny]-'A')&1)==0)))
                            continue;
                        if (grid[nx][ny] >= 'a' && grid[nx][ny] <= 'f')
                            nke |= (1 << (grid[nx][ny] - 'a'));
                        if (nke == key)
                            return level;
                        if (!visited[nx][ny][nke]) {
                            visited[nx][ny][nke] = true;
                            queue[r][0] = nx;
                            queue[r][1] = ny;
                            queue[r++][2] = nke;
                        }
                    }
                }
            }
            level++;
        }
        return -1;
    }
};

到达每一个点时,在同一个点上,状态也不同,即持有的钥匙不同,所以把在位置上的点和状态结合作为一种新的点进行扩展。求收集满这个点的最短距离。每条边的权值都是1,所以用bfs找最短距离

电动车游城

class Solution {
public:
    static const int maxn=101;
    int distance[maxn][maxn];
    bool visited[maxn][maxn];
    vector<vector<vector<int>>>pragh;
    void build(int n,int cnt){
        for(int i=0;i<n;i++){
            pragh.push_back(vector<vector<int>>()); 
            for(int j=0;j<=cnt;j++){
                distance[i][j]=INT_MAX;
                visited[i][j]=false;
            }
        }
    }
      
    void insert(int u,int v,int w){
         pragh[u].push_back({v,w});
         pragh[v].push_back({u,w});
    }
    int electricCarPlan(vector<vector<int>>& paths, int cnt, int start, int end, vector<int>& charge) {
         int n=charge.size();
         build(n,cnt);
         for(auto edge:paths)
          insert(edge[0],edge[1],edge[2]);
         auto cmp=[](vector<int>a,vector<int>b){return a[2]>b[2];};
         priority_queue<vector<int>,vector<vector<int>>,decltype(cmp)>heap(cmp);
         distance[start][0]=0;
         heap.push({start,0,0});
         while(!heap.empty()){
             vector<int>rem=heap.top();
             heap.pop();
             int cur=rem[0];
             int power=rem[1];
             int cost=rem[2];
             if(visited[cur][power])
             continue;
             if(cur==end) return cost;
             visited[cur][power]=true;
             if(power<cnt){//充电,只充一个电,如果还要充继续迭代
                if(!visited[cur][power+1]&&cost+charge[cur]<distance[cur][power+1]){
                    distance[cur][power+1]=cost+charge[cur];
                    heap.push({cur,power+1,distance[cur][power+1]});
                }
             }
             for(auto edge:pragh[cur]){//不充电
                int next=edge[0];
                int restpower=power-edge[1];
                int nextcost=cost+edge[1];
                if(restpower>=0&&!visited[next][restpower]&&nextcost<distance[next][restpower]){
                    distance[next][restpower]=nextcost;
                    heap.push({next,restpower,nextcost});
                }
                
             }

         }
         return -1;

         

    }
};

每个节点的状态除了点不同,还有剩余的电量(花费的时间是由剩余的电量和不同的边决定的,不属于决定性变量)。由于边权值的不同,用dijkstra求最小花费即可

飞行路线

#include <iostream>
#include <vector>
#include <queue>
#include <climits>
using namespace std;
int n, m, k, s, t;
const int maxn = 10001;
const int maxk = 11;
int distanc[maxn][maxk];
bool visited[maxn][maxk];
vector<vector<vector<int>>>pragh;
void build(int n,int k) {
	for (int i = 0; i < n; i++) {
		pragh.push_back(vector<vector<int>>());
		for (int j = 0; j <= k; j++) {
			distanc[i][j] = INT_MAX;
			visited[i][j] = false;
		}
	}
}
void insert(int u, int v, int w) {
	pragh[u].push_back({ v,w });
	pragh[v].push_back({ u,w });
}
int main() {
	cin >> n >> m >> k >> s >> t;
	build(n, k);
	while (m--) {
		int a, b, c;
		cin >> a >> b >> c;
		insert(a, b, c);
	}
	auto cmp = [](vector<int>a, vector<int>b) {return a[2] > b[2]; };
	priority_queue<vector<int>, vector<vector<int>>, decltype(cmp)>heap(cmp);
	distanc[s][0] = 0;
	heap.push({ s,0,0 });
	while (!heap.empty()) {
		vector<int>rem = heap.top();
		heap.pop();
		int cur = rem[0];
		int num = rem[1];
		int cost = rem[2];
		if (cur == t) {
			cout << cost;
			return 0;
		}
		if (visited[cur][num]) continue;
		visited[cur][num] = true;
		for (auto edge : pragh[cur]) {
			int nextnode = edge[0];
			int nums1 = num + 1;
			int nums2 = num;
			int nextcost1 = cost;
			int nextcost2 = cost + edge[1];
			if (!visited[nextnode][nums1] && nextcost1 < distanc[nextnode][nums1]) {
				distanc[nextnode][nums1] = nextcost1;
				heap.push({ nextnode,nums1,nextcost1 });
			}
			if (!visited[nextnode][nums2] && nextcost1 < distanc[nextnode][nums2]) {
				distanc[nextnode][nums2] = nextcost2;
				heap.push({ nextnode,nums2,nextcost2 });
			}

		}

	}
	return 0;

}

和上题的思路基本一致,用dijkstra算法分层拓展求解即可

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值