【PAT甲級筆記】001---Dijkstra算法類型題解

決定刷PAT,已經有一段時間,因爲各種事情耽擱,就沒能空下來,當發現是最後一次必須報名的時候,只有一個月的時間了…索性還不算太晚,這裏整一下別人的PAT資源和自己的目錄。

1.Dijkstra算法思路

假設它的起點是 a ,要求它到各點的最短距離
Dijkstra 思路是維護一個集合 s ,集合內的點是已經確定最短路的點,可以視爲一個大整體,每次操作找出距離這個集合最近的點加入集合中,並確定它的最短路爲它的上家的最短路+該邊權值,存在 dis 中。

該算法的核心有兩點

  • 1.找未被訪問且距離集合最短的點
  • 2.將所有未訪問點到該點的距離進行更新,鬆弛邊長

算法需要的數據類型
dis[ ] :記錄到該點的最短路徑長度
vis[ ] : 標記該點是否已經被訪問,即是否在集合S中

僞代碼如下:

memset(v, 0, sizeof(v)); 
for(int i = 0; i < n; i++) d[i] = (i==0 ? 0 : INF); 
for(int i = 0; i < n; i++) {  
  int x, m = INF;  
  //3.如果y沒有被加入集合,且d[y]是最小的,則把y加入集合且x = y
  for(int y = 0; y < n; y++) 
    if(!v[y] && d[y] <= m) m = d[y], x = y; 
  v[x] = 1;  //新的點加入集合(這是更新之後的新x)
  //4.更新x相鄰的點的d[i],實際上這裏更新的是所有點,但是與x未相鄰的w[x][y]值是無窮大,不可能被更新
  for(int y = 0; y < n; y++) d[y] = min(d[y], d[x] + w[x][y]); 
}

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

As an emergency rescue team leader of a city, you are given a special map of your country. The map shows several scattered cities connected by some roads. Amount of rescue teams in each city and the length of each road between any pair of cities are marked on the map. When there is an emergency call to you from some other city, your job is to lead your men to the place as quickly as possible, and at the mean time, call up as many hands on the way as possible.

Input

Each input file contains one test case. For each test case, the first line contains 4 positive integers: N (<= 500) – the number of cities (and the cities are numbered from 0 to N-1), M – the number of roads, C1 and C2 – the cities that you are currently in and that you must save, respectively. The next line contains N integers, where the i-th integer is the number of rescue teams in the i-th city. Then M lines follow, each describes a road with three integers c1, c2 and L, which are the pair of cities connected by a road and the length of that road, respectively. It is guaranteed that there exists at least one path from C1 to C2.

Output

For each test case, print in one line two numbers: the number of different shortest paths between C1 and C2, and the maximum amount of rescue teams you can possibly gather.
All the numbers in a line must be separated by exactly one space, and there is no extra space allowed at the end of a line.

Sample Input

5 6 0 2
1 2 1 5 3
0 1 1
0 2 2
0 3 1
1 2 1
2 4 1
3 4 1

Sample Output

2 4

這裏給出柳神的代碼和解析

#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;
}

發佈了73 篇原創文章 · 獲贊 38 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章