“ Ctrl AC!一起 AC!”
目录
Dijkstra一般用于解决单源最短路问题
ONE.求起点到达其他点的最短距离
策略:
设置集合S存放已访问的顶点,然后进行若干次以下操作,直到点全访问完毕。
1.每次从未访问的点中选择一个距离起点最近的点,加入S。
2.以新加入的点为介点进行路径优化。
具体实现:
1.bool型vis数组表示某点有没有访问过。
2.int型d数组代表起点到达该点的最短距离,初始化为inf,起点到起点距离则为零。
邻接矩阵版:
int n,G[MAXV][MAXV]; //n为顶点数,MAXV为最大顶点数
int d[MAXV]; //起点到某点的最短路径长度
bool vis[MAXV]; //初始为false表示没走过
void Dijkstra(int s){
fill(d,d+MAXV,INF); //初始距离边为无限大
d[s]=0; //起点到起点的距离为零
for(int i=0;i<n;i++){ //循环n次
int u=-1,MIN=INF;
for(int j=0;j<n;j++){
if(vis[j]==false&&d[j]<MIN){
u=j;
MIN=d[j];
}
}
if(u==-1) return;
vis[u]=true;
for(int v=0;v<n;v++){
if(vis[v]==false&&G[u][v]!=INF&&d[u]+G[u][v]<d[v]){
d[v]=d[u]+G[u][v];
}
}
}
}
邻接表版:
struct Node{
int v,dis; //v为边的目标顶点,dis为边权
};
vector<Node> Adj[MAXV]; //Adj[u]存放从顶点u出发可以到达的所有顶点
int n; //顶点数
int d[MAXV];
bool vis[MAXV];
void Dijkstra(int s){
fill(d,d+MAXV,INF);
d[s]=0;
for(int i=0;i<n;i++){
int u=-1,MIN=INF;
for(int j=0;j<n;j++){
if(vis[j]==false&&d[j]<MIN){
u=j;
MIN=d[j];
}
}
if(u==-1) return;
vis[u]=true;
for(int j=0;j<Adj[u].size();j++){
int v=Adj[u][j].v;
if(vis[v]==false&&d[u]+Adj[u][j].dis<d[v]){
d[v]=d[u]+Adj[u][j].dis;
}
}
}
}
注意点:
1.此方法不能解决边权为负数的情况
2.可以用最小堆或priority_queue解决第二循环---找最小的d[u],减少复杂度
TWO.求具体的最短路径
第一步:我们先在优化的时候记录一下前驱结点
int n,G[MAXV][MAXV];
int d[MAXV];
int pre[MAXV]; //pre[v]表示从起点到v的路径上 v的前一个结点
bool vis[MAXV];
void Dijkstra(int s){
fill(d,d+MAXV,INF);
for(int i=0;i<n;i++) pre[i]=i; //初始化为自己
d[s]=0;
for(int i=0;i<n;i++){
int u=-1,MIN=INF;
for(int j=0;j<n;j++){
if(vis[j]==false&&d[j]<MIN){
u=j;
MIN=d[j];
}
}
if(u==-1) return;
vis[u]=true;
for(int v=0;v<n;v++){
if(vis[v]==false&&G[u][v]!=INF&&d[u]+G[u][v]<d[v]){
d[v]=d[u]+G[u][v];
pre[v]=u;
}
}
}
}
第二步:得到前驱结点后可以通过递归或栈得到正确的路径:
void DFS(int s,int v){ //s为起点,v为当前的点
if(v==s){
printf("%d\n",s);
return;
}
DFS(s,pre[v]);
printf("%d\n",v);
}
THREE.求最短路径共有几条
只需新增一个num数组:num[i]表示起点到点i的最短路劲的条数,初始化num[起点]为1,其他为0
d[u]+G[u][v]<d[v]时,num[u]=num[v]
d[u]+G[u][v]==d[v]时,num[u]+=num[v]
for(int v=0;v<n;v++){
if(vis[v]==false&&G[u][v]!=INF){
if(d[u]+G[u][v]<d[v]){
d[v]=d[u]+G[u][v];
num[v]=num[u];
}
else if(d[u]+G[u][v]==d[v]){
num[v]+=num[u];
}
}
}
FOUR.考虑新增边权或点权求最优
1.新增边权:
如果一个二维数组a[u][v]表示u到v的新增边权,则同原边权一样开个数组表示c[u]表示从起点到u的新边权总和的最大值或最小值。
例:cost[u][v]表示u到v这条路的路费,要求费用最小的最短路径(先满足最短),我们新开一个数组c[],c[u]表示从起点到点u的最小花费,初始化c[起点]=0,其他为INF。
for(int v=0;v<n;v++){
if(vis[v]==false&&G[u][v]!=INF){
if(d[u]+G[u][v]<d[v]){
d[v]=d[u]+G[u][v];
c[v]=c[u]+cost[u][v];
}
else if(d[u]+G[u][v]==d[v]&&c[u]+cost[u][v]<c[v]){
c[v]=c[u]+cost[u][v];
}
}
}
2.新增点权:
例:weight[u]表示点u的物资数量,新增一个数组w[],w[u]表示从起点到点u可以收集到的最多的物资;初始化时w[s]=weight[u],其余为0。
for(int v=0;v<n;v++){
if(vis[v]==false&&G[u][v]!=INF){
if(d[u]+G[u][v]<d[v]){
d[v]=d[u]+G[u][v];
w[v]=w[u]+weight[v];
}
else if(d[u]+G[u][v]==d[v]&&w[u]+weight[v]>w[v]){
w[v]=w[u]+weight[v];
}
}
}
感谢阅读!!!
“ Ctrl AC!一起 AC!”