app的制作需要多少钱青岛seo推广专员
前言
这次的这些题感觉就会比前几篇要简单一点了,大多都是是背模板。
一、Dijkstra算法
Dijkstra算法是用在权值无负数的图上,用来找最短距离的算法。
1.模板——网络延迟时间
class Solution {
public://邻接表建图vector<vector<vector<int>>>graph;static bool cmp(vector<int>&a,vector<int>&b){return a[1]>b[1];}int networkDelayTime(vector<vector<int>>& times, int n, int k) {int m=times.size();build(n);//建图for(int i=0;i<m;i++){graph[times[i][0]].push_back({times[i][1],times[i][2]});}vector<int>distance(n+1,INT_MAX);distance[k]=0;vector<bool>visited(n+1,false);priority_queue<vector<int>,vector<vector<int>>,decltype(&cmp)>heap(cmp);heap.push({k,0});while(!heap.empty()){int u=heap.top()[0];heap.pop();if(!visited[u]){visited[u]=true;for(int i=0;i<graph[u].size();i++){int v=graph[u][i][0];int w=graph[u][i][1];if(!visited[v]&&distance[u]+w<distance[v]){distance[v]=distance[u]+w;heap.push({v,distance[v]});}}}}int ans=INT_MIN;for(int i=1;i<=n;i++){ans=max(ans,distance[i]);}return ans==INT_MAX?-1:ans;}void build(int n){graph.resize(n+1);}
};
Dijkstra算法的过程就是,首先设置distance数组存起点到每个点的最短距离,那么为了每次求最短,所以初始时每个点都设置成无穷大。之后除了还要设置一个visited数组记录来没来过,还要借助一个以distance为排序的小根堆。
之后只要堆不为空,每次取堆顶元素,没来过就去当前节点的所有边看,如果到当前节点的距离加上边权比下一个点的距离更小,即通过这条路能把去下一个节点的距离变得更小,就更新并入堆。
2.模板——【模板】单源最短路径(标准版)
#include<bits/stdc++.h>
using namespace std;//邻接表建图
vector<vector<vector<int>>>graph; static bool cmp(vector<int>&a,vector<int>&b)
{return a[1]>b[1];
}void build(int n)
{graph.resize(n+1);
}void solve(int n,int m,int s,vector<vector<int>>&edges)
{build(n);//建图for(int i=0;i<m;i++){graph[edges[i][0]].push_back({edges[i][1],edges[i][2]}); } vector<int>distance(n+1,INT_MAX);distance[s]=0;vector<bool>visited(n+1,false);//小根堆 priority_queue<vector<int>,vector<vector<int>>,decltype(&cmp)>heap(cmp);heap.push({s,0});while(!heap.empty()){int u=heap.top()[0];heap.pop();if(!visited[u]){visited[u]=true;for(int i=0;i<graph[u].size();i++){int v=graph[u][i][0];int w=graph[u][i][1];if(!visited[v]&&distance[u]+w<distance[v]){distance[v]=distance[u]+w;heap.push({v,distance[v]});}}}}//输出for(int i=1;i<=n;i++){cout<<distance[i]<<" "; }
}void read()
{int n,m,s;cin>>n>>m>>s;vector<vector<int>>edges(m,vector<int>(3));for(int i=0;i<m;i++){cin>>edges[i][0]>>edges[i][1]>>edges[i][2];}solve(n,m,s,edges);
}int main()
{ios::sync_with_stdio(false);cin.tie(nullptr);read();return 0;
}
这个题跟上个题一样。
其实观察一下就能发现,在数据结构与算法:宽度优先遍历中提到的01bfs其实就是Dijkstra的特殊情况,而当边权只有0和1时,就可以用双端队列代替小根堆。
3.最小体力消耗路径
class Solution {
public:vector<int>move={-1,0,1,0,-1};static bool cmp(vector<int>&a,vector<int>&b){return a[2]>b[2];}int minimumEffortPath(vector<vector<int>>& heights) {int n=heights.size();int m=heights[0].size();vector<vector<int>>distacne(n,vector<int>(m,INT_MAX));distacne[0][0]=0;vector<vector<bool>>visited(n,vector<bool>(m,false));priority_queue<vector<int>,vector<vector<int>>,decltype(&cmp)>heap(cmp);heap.push({0,0,0});while(!heap.empty()){int x=heap.top()[0];int y=heap.top()[1];int c=heap.top()[2];heap.pop();if(!visited[x][y]){//剪枝if(x==n-1&y==m-1){return c;}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]){int nc=max(c,abs(heights[x][y]-heights[nx][ny]));if(nc<distacne[nx][ny]){distacne[nx][ny]=nc;heap.push({nx,ny,nc});} }}}}return distacne[n-1][m-1];}
};
这个题其实还是Dijkstra的模板题,只是计算距离的方法不同。
所以只需要统计最大的绝对差,即目前为止的绝对差和当前点和下一个点的绝对差的最大值,然后以此为去往下一个点的代价更新即可。
4.水位上升的泳池中游泳
class Solution {
public:vector<int>move={-1,0,1,0,-1};static bool cmp(vector<int>&a,vector<int>&b){return a[2]>b[2];}int swimInWater(vector<vector<int>>& grid) {int n=grid.size();vector<vector<int>>distance(n,vector<int>(n,INT_MAX));distance[0][0]=grid[0][0];vector<vector<bool>>visited(n,vector<bool>(n,false));priority_queue<vector<int>,vector<vector<int>>,decltype(&cmp)>heap(cmp);heap.push({0,0,distance[0][0]});while(!heap.empty()){int x=heap.top()[0];int y=heap.top()[1];int t=heap.top()[2];heap.pop();if(!visited[x][y]){//剪枝if(x==n-1&&y==n-1){return t;}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<n&&!visited[nx][ny]){int nt=max(0,grid[nx][ny]-t);if(distance[x][y]+nt<distance[nx][ny]){distance[nx][ny]=distance[x][y]+nt;heap.push({nx,ny,distance[nx][ny]});}}}}}return distance[n-1][n-1];}
};
这个题其实也是模板,不同的还是统计边权,即去往下一个点的时间为0和下一个点水升上来的时间减去来到当前点的时间取最大值。
二、分层图最短路
当最短距离需要满足其他条件时,可以将来到相同点时不同的已满足条件的情况看作不同的节点。
1.获取所有钥匙的最短路径
class Solution {
public:vector<int>move={-1,0,1,0,-1};int shortestPathAllKeys(vector<string>& grid) {int n=grid.size();int m=grid[0].length();queue<vector<int>>node;int key=0;//用位信息压缩钥匙状态//初始化for(int i=0;i<n;i++){for(int j=0;j<m;j++){if(grid[i][j]=='@'){node.push({i,j,0});}//是钥匙if(grid[i][j]>='a'&&grid[i][j]<='f'){key|=1<<(grid[i][j]-'a');}}}//初始化visited -> 考虑钥匙状态vector<vector<vector<bool>>>visited(n,vector<vector<bool>>(m,vector<bool>(key+1,false)));int level=0;while(!node.empty()){level++;int size=node.size();for(int i=0;i<size;i++){int x=node.front()[0];int y=node.front()[1];int k=node.front()[2];node.pop();for(int j=0;j<4;j++){int nx=x+move[j];int ny=y+move[j+1];int nk=k;if(nx>=0&&nx<n&&ny>=0&&ny<m&&grid[nx][ny]!='#'){//是锁且没钥匙if(grid[nx][ny]>='A'&&grid[nx][ny]<='F'&&(nk&(1<<(grid[nx][ny]-'A')))==0){continue;}//是钥匙if(grid[nx][ny]>='a'&&grid[nx][ny]<='f'){nk|=1<<(grid[nx][ny]-'a');}//剪枝if(nk==key){return level;}if(!visited[nx][ny][nk]){visited[nx][ny][nk]=true;node.push({nx,ny,nk});}}}}}return -1;}
};
这个题除了距离还需要钥匙这个条件,再观察数据范围可以发现钥匙就六个,所以考虑用位信息来压缩已有钥匙的状态。
首先需要遍历格子找出总共的钥匙数和起点位置。由于已有钥匙的状态会看作不同节点,所以visited数组要再升一维。之后去跑宽度优先遍历,注意当有锁但没钥匙时要直接跳过不看,是钥匙的话就更新状态即可。
2.电动车游城市
class Solution {
public://邻接表建图vector<vector<vector<int>>>graph;static bool cmp(vector<int>&a,vector<int>&b){return a[2]>b[2];}int electricCarPlan(vector<vector<int>>& paths, int cnt, int start, int end, vector<int>& charge) {int n=charge.size();int m=paths.size();//建图 -> 无向图!graph.resize(n);for(int i=0;i<m;i++){graph[paths[i][0]].push_back({paths[i][1],paths[i][2]});graph[paths[i][1]].push_back({paths[i][0],paths[i][2]});}vector<vector<int>>distance(n,vector<int>(cnt+1,INT_MAX));distance[start][0]=0;vector<vector<bool>>visited(n,vector<bool>(cnt+1,false));priority_queue<vector<int>,vector<vector<int>>,decltype(&cmp)>heap(cmp);heap.push({start,0,0});//当前点 来到当前点的电量 花费的时间while(!heap.empty()){int cur=heap.top()[0];int power=heap.top()[1];int time=heap.top()[2];heap.pop();if(!visited[cur][power]){//剪枝if(cur==end){return time;}visited[cur][power]=true;//能充电 -> 充一格if(power<cnt){if(!visited[cur][power+1]&&time+charge[cur]<distance[cur][power+1]){distance[cur][power+1]=time+charge[cur];heap.push({cur,power+1,distance[cur][power+1]});}}//不充电for(int i=0;i<graph[cur].size();i++){int next=graph[cur][i][0];int restPower=power-graph[cur][i][1];int nextTime=time+graph[cur][i][1];if(restPower>=0&&!visited[next][restPower]&&nextTime<distance[next][restPower])//能到且时间更短{distance[next][restPower]=nextTime;heap.push({next,restPower,nextTime});}}}}return -1;}
};
这个题需要考虑的就是剩余电量,所以每来到一个点都要分充电和不充电两种情况考虑。因为不同的剩余电量属于不同状态,所以每次充电时不用讨论充几格,而是同一只充一格,剩下的留给之后去充。之后根据充电和不充电对应的时间代价去跑Dijkstra即可。
3.飞行路线
#include<bits/stdc++.h>
using namespace std;static bool cmp(vector<int>&a,vector<int>&b)
{return a[2]>b[2];
}//邻接表建图
vector<vector<vector<int>>>graph;int solve(int n,int m,int k,int s,int t,vector<vector<int>>&edges)
{//建图 -> 无向图! graph.resize(n);for(int i=0;i<m;i++){graph[edges[i][0]].push_back({edges[i][1],edges[i][2]});graph[edges[i][1]].push_back({edges[i][0],edges[i][2]}); } vector<vector<int>>distance(n,vector<int>(k+1,INT_MAX));distance[s][0]=0;vector<vector<bool>>visited(n,vector<bool>(k+1,false));priority_queue<vector<int>,vector<vector<int>>,decltype(&cmp)>heap(cmp); heap.push({s,0,0});while(!heap.empty()){int cur=heap.top()[0];int use=heap.top()[1];int cost=heap.top()[2];heap.pop();if(!visited[cur][use]){if(cur==t){return cost;}visited[cur][use]=true; for(int i=0;i<graph[cur].size();i++){int next=graph[cur][i][0];//能用 -> 用一次 int nextUse=use+1;int nextCost=0;if(use<k&&!visited[next][nextUse]&&nextCost+distance[cur][use]<distance[next][nextUse]){distance[next][nextUse]=nextCost+distance[cur][use];heap.push({next,nextUse,distance[next][nextUse]});}//不用nextUse=use;nextCost=graph[cur][i][1];if(!visited[next][nextUse]&&nextCost+distance[cur][use]<distance[next][nextUse]){distance[next][nextUse]=nextCost+distance[cur][use];heap.push({next,nextUse,distance[next][nextUse]});}}}}return -1;
}void read()
{int n,m,k;cin>>n>>m>>k;int s,t;cin>>s>>t;vector<vector<int>>edges(m,vector<int>(3));for(int i=0;i<m;i++){cin>>edges[i][0]>>edges[i][1]>>edges[i][2];}cout<<solve(n,m,k,s,t,edges);
}int main()
{ios::sync_with_stdio(false);cin.tie(nullptr);read();return 0;
}
这个题就是讨论用和不用两种情况即可。
总结
其实这几道题归根结底还是模板,重点是要通过分析题目想到可以用Dijkstra和分层图最短路解决。