【PAT甲级笔记】001---Dijkstra算法类型题解

决定刷PAT,已经有一段时间,因为各种事情耽搁,就没能空下来,当发现是最后一次必须报名的时候,只有一个月的时间了…索性还不算太晚,这里整一下别人的PAT资源和自己的目录。

1.Dijkstra算法思路

假设它的起点是 a ,要求它到各点的最短距离
Dijkstra 思路是维护一个集合 s ,集合内的点是已经确定最短路的点,,每次操作找出距离这个集合最近的点加入集合中,将所有未访问点到该点的距离进行更新,松弛边长,存在 dis 中。

初始化阶段:将dis里均置为INF,并将出发点dis置为0,以便于直接进入循环
该算法的核心为三个步骤

  • 1.找未被访问且距离集合最短的点
  • 2.将该点加入集合
  • 3.将所有未访问点到该点的距离进行更新,松弛边长

算法需要的数据类型
dis[ ] :记录到该点的最短路径长度
vis[ ] : 标记该点是否已经被访问,即是否在集合S中

伪代码如下:

//初始化
memset(v, 0, sizeof(v)); 
for(int i = 0; i < n; i++) dis[i] = (i==0 ? 0 : INF); 

for(int i = 0; i < n; i++) {  
  int x, min = INF; 
  //1.如果y没有被加入集合,且d[y]是最小的,则把y加入集合且x = y
  for(int y = 0; y < n; y++) 
    if(!vis[y] && dis[y] <= min) min = dis[y], x = y; 
    
  //2.新的点加入集合(这是更新之后的新x)
  vis[x]=true;
  
  //3.更新x相邻的点的d[i],实际上这里更新的是所有点,但是与x未相邻的w[x][y]值是无穷大,不可能被更新
  for(int y = 0; y < n; y++)
   dis[y] = min(dis[y], dis[x] + w[x][y]); 
}
为什么仅需要将start点距离初始为0?

不那么做的时候,进入dij循环时,由于所有的dis[]均为初始化inf,这会导致第一个进入的为第一个inf点,这不符合我们的预期,而将点初始化为0,可以在循环时,首先进入该点,进行操作,完美初始化。

2.dijkstra算法提高以及对应模板:

该算法的考察一般都不会只考一个参考标准,一般存在点权,边权和求最短路径的条数。在使用模版时,仅修改对应的第三步,前两步不变。
2.1存在点权和边权,求最短路径下最小花费:(求解模板一致)
将判断的条件进行修改即可。

int cost[MAXV][MAXV], c[MAXV];
fill(c, c + MAXV, INF);
c[s] = 0;
for (int v = 0; v < n; v++){
    #当选择点到循环到的点可达且循环点未被访问过(即未加入集合)
    if (!vis[v] && G[u][v] != INF){
        #如果距离一致,不需要更新边,则取点权或边权更小的,将更小的记录
        if (d[u] + G[u][v] == d[v] && c[u] + cost[u][v] < c[v]){
            c[v] = c[u] + cost[u][v];
        }
        else if (d[u] + G[u][v] < d[v]){
            d[v] = d[u] + G[u][v];
            c[v] = c[u] + cost[u][v];
        }
    }
}

2.2求最短路径条数:

int num[MAXV] = {0};
num[s] = 1;
for (int v = 0; v < n; v++){
    if (!vis[v] && G[u][v] != INF){
        if (d[u] + G[u][v] == d[v]{
            num[v] += num[u];
        }
        else if (d[u] + G[u][v] < d[v]){
            d[v] = d[u] + G[u][v];
            num[v] = num[u];
        }
    }
}

3. dijkstra PAT 甲级1003

这里给出柳神的代码和解析

#include <stdio.h>
#include <iostream>
#include <algorithm>
using namespace std;
int n, m, c1, c2;
int e[510][510], weight[510], dis[510], num[510], w[510];
bool visit[510];
const int inf = 99999999;
int main() {
    scanf("%d%d%d%d", &n, &m, &c1, &c2);
    for(int i = 0; i < n; i++)
        scanf("%d", &weight[i]);
    fill(e[0], e[0] + 510 * 510, inf);
    fill(dis, dis + 510, inf);
    int a, b, c;
    for(int i = 0; i < m; i++) {
        scanf("%d%d%d", &a, &b, &c);
        e[a][b] = e[b][a] = c;
    }
   
    #必须要有个值首先进入,这里初始点为0点,将距离为0,进入循环时该点为第一个被考察。
    dis[c1] = 0;
    w[c1] = weight[c1];
    num[c1] = 1;
    
   
    for(int i = 0; i < n; i++) {
        int u = -1, minn = inf;
        for(int j = 0; j < n; j++) {
            if(visit[j] == false && dis[j] < minn) {
                u = j;
                minn = dis[j];
            }
        }
        if(u == -1) break;
        visit[u] = true;
        for(int v = 0; v < n; v++) {
            if(visit[v] == false && e[u][v] != inf) {
                if(dis[u] + e[u][v] < dis[v]) {
                    dis[v] = dis[u] + e[u][v];
                    num[v] = num[u];
                    w[v] = w[u] + weight[v];
                    //这问题里:即给了权重,又要求条数,所以需要w[],num[]两个数组
                } else if(dis[u] + e[u][v] == dis[v]) {
                    num[v] = num[v] + num[u];
                    if(w[u] + weight[v] > w[v])
                        w[v] = w[u] + weight[v];
                }
            }
        }
    }
    printf("%d %d", num[c2], w[c2]);
    return 0;
}

4.PAT里其他题的代码:

PAT 甲级1018

#include<cstdio>
#include<vector>
#include<iostream>
#include<cmath>

using namespace std;
const int inf=99999999;
int cmax,n,sp,m,back1,need1,backmin,needmin;
int e[510][510],dis[510],weight[510];
vector<int> path, temppath, pre[510];
bool vis[510];


void djstra()
{
    dis[0] = 0;
    for(int j=0; j<=n; j++)
    {
        //step 1;
        int mini = -1;
        int minv = inf;
        for(int i=0; i<=n; i++)
        {
             if(vis[i]==0 && dis[i]<minv)
             {
                 minv = dis[i];
                 mini = i;
             }
        }

        //step 2;
        vis[mini] = 1;
        for(int i=0; i<=n;i++)
        {
            //没有这一判断也能过,为最优子集并且不可达本身不会是最小的
            if(vis[i]==0 && e[mini][i]!= inf)
            {
                if(dis[i] > e[mini][i]+ dis[mini])
                {
                    dis[i] = e[mini][i]+ dis[mini];
                    pre[i].clear();
                    pre[i].push_back(mini);
                }
                else if(dis[i] == e[mini][i]+ dis[mini])
                {
                    pre[i].push_back(mini);
                }
            }

        }
    }
    //printf("%d", pre[sp][0]);

}

void dfs(int v)
{
    temppath.push_back(v);
    if(v ==0)
    {
        back1=need1=0;
        for(int i=temppath.size()-1; i>=0;i--)
        {
            if(back1+weight[temppath[i]] > 0)
            {
               back1+=weight[temppath[i]];
            }
            else //if(abs(back1+weight[temppath[i]]) > need1 )
            {
                need1-= back1+weight[temppath[i]];
                back1=0;
            }
        }
        if(need1 < needmin)
        {
            needmin = need1;
            backmin = back1;
            path = temppath;
        }
        if(need1 == needmin && back1< backmin)
        {
            needmin = need1;
            backmin = back1;
            path = temppath;
        }
        temppath.pop_back();
        return;
    }
//这一步应该是遍历它的所有叶子节点
    for(int i=0; i<pre[v].size(); i++)
        dfs(pre[v][i]);
    temppath.pop_back();
}


int main()
{
    //init
    fill(e[0], e[0]+510*510, inf);
    fill(dis,dis+510,inf);
    fill(vis,vis+510,0);
    backmin = needmin = inf;
    scanf("%d%d%d%d", &cmax, &n, &sp, &m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d", &weight[i]);
        weight[i]-= cmax/2;
    }
    for(int i=0;i<m;i++)
    {
        int a,b;
        scanf("%d%d", &a, &b);
        scanf("%d", &e[a][b]);
        e[b][a]= e[a][b];
    }

    //djstra+dfs
    djstra();
    dfs(sp);

    printf("%d 0", needmin);
    for(int i=path.size()-2; i>=0;i--)
        printf("->%d",path[i]);
    printf(" %d",backmin);
    return 0;
}

PAT甲级 1030

#include<cstdio>
#include<vector>
#include<iostream>

using namespace std;
const int inf = 99999999;

int cost[510][510],e[510][510],dis[510];
int n,m,s,d,costmin,dismin;
bool vis[510];
vector<int> pre[510],temppath,path;

void djistra()
{
    //init
    fill(dis, dis+510, inf);
    dis[s] = 0;

    for(int i=0; i<n;i++)
    {
        //step 1 find shortest point
        int mini=-1;
        int minv=inf;
        for(int j=0;j<n;j++)
        {
            if(vis[j]==false && minv > dis[j])
            {
                mini = j;
                minv = dis[j];
            }
        }

        //step 2 update not include edge
        vis[mini]= true;
        for(int v=0;v<n;v++)
        {
            if(vis[v]==false && e[mini][v]!=inf)
            {
                if(e[mini][v]+dis[mini]<dis[v])
                {
                   dis[v] = e[mini][v]+dis[mini];
                   pre[v].clear();
                   pre[v].push_back(mini);

                }
                else if(e[mini][v]+dis[mini]==dis[v])
                {
                    pre[v].push_back(mini);
                }
            }
        }
    }
    dismin=dis[d];
}

void dfs(int v)
{

    temppath.push_back(v);
    if(v == s)
    {
     int costv = 0;
     //this issue point algorithm
     for(int j =1; j<temppath.size();j++)
       {
           int id1=temppath[j];
           int id2=temppath[j-1];
           costv +=cost[id1][id2];
       }
     if(costv <costmin)
     {
         costmin = costv;
         path  = temppath;
     }
     temppath.pop_back();
     return ;
    }
     for(int i=0;i<pre[v].size();i++)
        dfs(pre[v][i]);
    temppath.pop_back();
}

int main()
{
    //init
    fill(cost[0], cost[0]+510*510, inf);
    fill(e[0], e[0]+510*510, inf);
    costmin = inf;

    scanf("%d%d%d%d", &n, &m, &s, &d);
    int a,b;
    for(int i=0;i<m;i++)
    {
        scanf("%d%d",&a, &b);
        scanf("%d%d", &e[a][b], &cost[a][b]);
        e[b][a]= e[a][b];
        cost[b][a]= cost[a][b];
    }
    //printf("cos %d",cost[0][1]);


    djistra();
    dfs(d);

    //output
    for(int i= path.size()-1;i>=0; i--)
        printf("%d ",path[i]);
    printf("%d %d",dismin, costmin);
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值