A-Age of Moyu HDU-6386 (Dijkstra + heap optimization)

Insert picture description here
Insert picture description here
To solve this problem, you can use dfs+bfs, but I don’t, so I only know the dijkstra algorithm;
to solve this problem, you must understand the core idea of ​​Dijkstra’s algorithm. I understand it like this:
I can do anything For example, a graph: Insert picture description here
such as this graph, then find the shortest path from 1 to 6; of course, because this point can rarely be stored in a two-dimensional array;
Dijkstra thought: I first use the dis[] array to store all the points that can be reached by 1. Then I set other points as INF (usually INF command is 0x3f3f3f3f);
Insert picture description here
then I can use the existing reachable point as a transfer station and then find other reachable points, and then update the dis array value every time;
For example: we start from 2 and enumerate all the points that 2 can reach (of course, because 1 has been found, we should use an array book[1]=1 to mark that it has gone);
then 2 can be reached There are only 4 and 3, then compare the distance from 1 to 4 (INF) and the distance from 1 to 2 to 4 (1+8). Because it is less than INF, dis[4] is updated to 9; the
same is true for heap 3. After that, every time I used to be a transit point was marked, because it was impossible to walk repeatedly.
So the question is, how to think about this idea?
First of all, I should make it clear that all points except the first point will be used as transfer stations, so the big for loop is 1—n-1 points. Note: the big for here is only used to indicate that each point needs to be used as Transit station; there is no necessary logical connection with the content inside;
so the simplest Dijkstra algorithm comes out (because this algorithm does not consider the situation of the array is too large):

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define INF 0x3f3f3f3f
int Map[1000][1000],n,m;//Map用来记录a点到b点的路径值,n表示有多少个点,m表示给出多少条边 
 int dis[1000],book[1000];//dis是用来表示 start点到其他点的距离,book用来标记 循环到这个点的是否被标记过 
void dijkstra(int start){//最简单的dijkstra算法(采用矩阵形式) 
	 //首先求出这个点到其他点的距离用一个for
	 int Min=INF;
	 for(int i=1;i<=n;i++){//初始化从start这点开始的所有能与start点可达的点的距离 
	 	  dis[i]=Map[start][i];//注意这里dis和Map只是一个逻辑关系相联系 
	 }
	 //这里需要枚举n-1个点,因为开始点不需要算
	 //所以用一个大的for来循环
	 book[start]=1;//标记开始点已经作为中转站过了 
	 for(int i=1;i<=n-1;i++){
	 	//找出第一个到start的最短的点
		 //并且标记已经选过了
		  Min=INF;
		  int u;//记录到start最短的点 
		  for(int j=1;j<=n;j++){
		  	  if(book[j]==0&&dis[j]<Min){//去搜寻能与start有直接边的点的最小距离//这里最好头脑里面想着无向图 
		  	  	   Min=dis[j];
		  	  	   u=j;
				}
		  }
		  book[u]=1;//表示u这个点已经作为中转站了 
		  //然后找到以u点为中转站的其他点到start的距离和经过u这个点的距离,并比较大小
		for(int e=1;e<=n;e++){//这里e是end的简写,方便理解表示以u作为中转站所能到达的另一个点 
			  if(Map[u][e]<INF){
			  	  if(dis[e]>dis[u]+Map[u][e]){//这就是上面分析的比较 
			  	  	 dis[e]=dis[u]+Map[u][e];
					}
			  }
		}
	 } 
}
void init(int n){
	for(int i=1;i<=n;i++)
	{
		  for(int j=1;j<=n;j++){
		  	  if(i==j) Map[i][j]=0;//自己到自己就是0 
		  	  else Map[i][j]=INF;//其他的设为INF 
		  }
	}
}
void read(int m)//这里用来输入,因为函数分开写更好理解它的功能
{
		int a,b,c;
	for(int i=1;i<=m;i++){
		  cin>>a>>b>>c;
		  Map[a][b]=c;//令为无向图
		  Map[b][a]=c; 
	}
} 
int main(){
	cin>>n>>m;
	init(n);//这里虽然n,m为全局变量,但是这样写参数传递更加好理解; 
	read(m);
	dijkstra(1);//从第一个点开始(当然这里也可以从别的点开始) 
	for(int i=2;i<=n;i++){//输出到其他点的最短距离 
		 cout<<"1到"<<i<<"的最短距离为:"<<dis[i]<<"\n";
	}
	cout<<endl;
	return 0;
}

According to the above figure, the result is like this: Insert picture description here
then the above is just the simplest Dijkstra algorithm, which is far from enough for the AC problem, so how to optimize it?
The priority queue + structure + adjacency list + Dijkstra will be used here; it is very complicated to think about, but it is really useful if you really understand it; the AC code for this problem is below, but I believe that Dijkstra + heap is used Optimization will definitely improve the efficiency of the algorithm:

#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
typedef long long ll;
ll n,m;
struct End_Dis{
	ll to,d,c;//to表示到的点,d表示到这个兵兵(也可以理解为距离问题),c用来记录是不是有这个兵兵在这条路上 
	End_Dis(ll a,ll b,ll cc=0):to(a),d(b),c(cc){}
	bool operator < (const End_Dis tt) const{
		return d>tt.d;//这个比较特殊,因为优先队列是由大到小,所以我写了一个由大到小的,然后这样会让优先队列由小到大排列(特别的地方) 
	}
};
vector<End_Dis> a[100000*2];
ll book1[200010];//用来标记这个点扫过没有
ll Dijkstra(ll start){
	ll dis[200010];
	 for(ll i=1;i<=n;i++) dis[i]=INF;//先初始化为INF
	 book1[1]=0;//表示这个点走过了
	 dis[1]=0;//自己到自己距离为0;
	 priority_queue<End_Dis> q;//建立一个优先队列
	 q.push(End_Dis(1,0));//把第一个点放进去
	  while(!q.empty()) {//循环为非空 
        End_Dis t=q.top();
        q.pop();
        if(book1[t.to]) continue;//表示这个点使用过没有 
        for(ll i=0;i<a[t.to].size();i++){//以这个点位中转站,枚举其他点更新1----其他点的最短距离 
            End_Dis x=a[t.to][i];//取出对象 
            if((t.c!=x.d)+t.d<dis[x.to]) {//1到中转站h的距离+前面是否出现过的值是否小于  1---x的距离 
                dis[x.to]=(t.c!=x.d)+t.d;
                q.push(End_Dis(x.to,dis[x.to],x.d));//找到了之后然后让中转站后面的点进队 
            }
        }
    }
    return dis[n];
}
void read(ll m){
	ll aa,bb,cc;
	  for(ll i=1;i<=m;i++){
	  	  scanf("%lld %lld %lld",&aa,&bb,&cc);
	  	  a[aa].push_back(End_Dis(bb,cc));
			a[bb].push_back(End_Dis(aa,cc)); 
	  }
}
void init(ll n){
	for(ll i=1;i<=n;i++){
		  a[i].clear();
	}
}
int main(){
	while(~scanf("%lld %lld",&n,&m)){
		init(n);
	read(m);
	int ans=Dijkstra(1);
	if(ans==INF)puts("-1");
	else printf("%lld\n",ans);
    }
	return 0;
}

Of course, this question gave me enlightenment. I used to understand the Dijkstra algorithm, but that is very shallow. I believe this heap optimization can be used to solve the problem of the graph I gave earlier:
just change the code. :

#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
typedef long long ll;
ll n,m;
struct End_Dis{
	ll to,d;//to表示到的点,d表示为前一个点到to这个点的距离 
	End_Dis(ll a,ll b):to(a),d(b){}///这里构造函数有便于加点 
	bool operator < (const End_Dis tt) const{//这里在使用优先队列的时候很有用 
		return d>tt.d;//这个比较特殊,因为优先队列是由大到小,所以我写了一个由大到小的,然后这样会让优先队列由小到大排列(特别的地方) 
	}
};
vector<End_Dis> a[100000*2];//用邻接表来存储一个点能到的其他点比如:a[aa][i].to表示aa这个点到to这个点 
ll book1[200010];//用来标记这个点扫过没有//用来标记这个点是否作为过中转站 
ll Dijkstra(ll start){
	ll dis[200010];//表示距离 
	 for(ll i=1;i<=n;i++) dis[i]=INF;//先初始化为INF
	 book1[start]=0;//表示这个点走过了//这里需要注意了 因为这里是和最简单的Dijkstra算法不同点 
	 dis[start]=0;//自己到自己距离为0;
	 priority_queue<End_Dis> q;//建立一个优先队列
	 q.push(End_Dis(start,0));//把第一个点放进去
	  while(!q.empty()) {//循环为非空 
        End_Dis t=q.top();
        q.pop();
        if(book1[t.to]) continue;//表示这个点使用过没有 
        for(ll i=0;i<a[t.to].size();i++){//以这个点位中转站,枚举其他点更新1----其他点的最短距离 
            End_Dis x=a[t.to][i];//取出t.to这个点所能到达的点的对象
			if(dis[x.to]>dis[t.to]+x.d){//start到x.to点的距离 和   start到t.to的距离与t.to到x.d的和的比较 
				//更新这个点的距离
				dis[x.to]=dis[t.to]+x.d;
				q.push(End_Dis(x.to,dis[x.to]));//把这个点加入优先队列中 
			}
        }
    }
    return dis[n];
}
void read(ll m){//因为为无向图 
	ll aa,bb,cc;
	  for(ll i=1;i<=m;i++){
	  	  scanf("%lld %lld %lld",&aa,&bb,&cc);
	  	  a[aa].push_back(End_Dis(bb,cc));
			a[bb].push_back(End_Dis(aa,cc)); 
	  }
}
void init(ll n){//每次清空 
	for(ll i=1;i<=n;i++){
		  a[i].clear();
	}
}
int main(){
	while(~scanf("%lld %lld",&n,&m)){
		init(n);
	   read(m);
	  int ans=Dijkstra(1);
	  if(ans==INF)puts("-1");//如果不能到n点那么久输出-1 
	 else printf("到n点的最短路径为:   %lld\n",ans);
    }
	return 0;
}

This is what I think is the best Dijkstra algorithm version (because the integrated use of struct + priority queue + clever Dijsktra algorithm, you can directly bring the template, call, finally sorted out (it seems that there is a save path problem (see me later) Add it)))Insert picture description here

Guess you like

Origin blog.csdn.net/qq_44555205/article/details/97963630