【PAT甲级、C++ Dijkstra、图单源最短路径】1003 Emergency (25分)

需要二刷【二刷了,更新在下面了】

Dijkstra邻接矩阵

# include <iostream>
# include <vector>
# include <algorithm>

using namespace std;

const int INF = 0xffff;

/*
城市数N 路数M 起始城市C1 终点城市C2
N个城市中的救援队数量
M条路(弧头 弧尾 路的长度(权重))
*/
typedef int INDEX;

struct CityGraph
{
    vector<int>         vexs;                // 城市   --顶点表(存储了顶点的信息)
    vector<vector<int>> arcs;                // 路     --邻接矩阵(存储了边的权值,不存在边就为无穷大INF ∞)
    int                 vexnum;              // 城市数 --图中的当前顶点数
    int                 arcnum;              // 路径数 --图中的当前边数
    INDEX C1;                                // 起始城市
    INDEX C2;                                // 终点城市
    
    CityGraph()
    {
        cin >> vexnum >> arcnum >> C1 >> C2;
        vexs = vector<int>(vexnum);
        for(int & i: vexs){
            cin >> i;
        }
        
        arcs = vector<vector<int>>(vexnum, vector<int>(vexnum, INF));
        int a, b, weight;
        for(int i=0;i<arcnum;i++){
            cin >> a >> b >> weight;  // a->b
            arcs[a][b] = weight;
            arcs[b][a] = weight;
        }
    }
    
    INDEX findMinDistIndex(const vector<bool> &S, const vector<int> &dist)
    {
        int idx = -1;
        int mindist = INF;
        for(int i=0;i<vexnum;i++){
            if(S[i] == false && dist[i] < mindist){
                mindist = dist[i];
                idx = i;
            }
        }
        
        return idx;
    }
    
    void finMinDistANDMaxTeamAmount()
    {
        int mindist;
        vector<bool> S   (vexnum, false);     // 标记i顶点是否加入到最短路径中(是否被访问过)
        vector<int>  dist(vexnum, INF-10);   // 每个dist[i]表示起始点C1到Ci的最短路径的长度
        vector<int>  num (vexnum, 0);       // 最短路径的数量(可能有相同长度的最短的路径、用这个代替path数组统计最短路径数量就可以了)
        vector<int>  team(vexnum, 0);      // 每个team[i]表示起始点C1到Ci的最大救援队数量
        
        dist[C1] = 0;
        num[C1]  = 1;
        team[C1] = vexs[C1];
        
        // 循环到所有顶点已加入最短路径
        for(int i=0;i<vexnum;i++)
        {
            mindist = findMinDistIndex(S, dist);
            if(mindist == -1)
                break;
                
            S[mindist] = true;
            
            for(int j=0;j<vexnum;j++){
                long pathdist = dist[mindist] + arcs[mindist][j];
                int  teammax  = team[mindist] + vexs[j];
                if(S[j] == false && dist[j] >  pathdist){
                    num[j] = num[mindist];
                    dist[j] = pathdist;
                    team[j] = teammax;
                }
                else
                if(S[j] == false && dist[j] == pathdist){
                    num[j]  += num[mindist];
                    team[j] = max(teammax, team[j]);
                }
            }   
        }
        
        cout << num[C2] << " ";
        cout << team[C2]<< endl;
    }
    
    void print_path_dist(vector<int> & dist,vector<int> & path)
    {
        printf("i    dist  path\n");
        for(int i=0;i<vexnum;i++){
            printf("%d    %2d    %2d\n", i, dist[i], path[i]);
        }
    }
    
    void showVexs()
    {
        for(int i: vexs){
            cout << i << " ";
        }cout << endl;
    }
    
    void showArcs()
    {
        for(vector<int> i: arcs){
            for(int j: i){
                if(j == INF)
                    cout << "∞ ";
                else
                    printf("%2d ", j);
            }cout << endl;
        }cout << endl;
    }
    
};

int main()
{
    CityGraph G;
//     G.showVexs();
//     G.showArcs();
    G.finMinDistANDMaxTeamAmount();
    
    return 0;
}

二刷 Dijkstra邻接表

# include <iostream>
# include <vector>
# include <algorithm>

using namespace std;

int N;  // 顶点数
int M;  // 边数
int C1; // 源点
int C2; // 终点

const int INF = 0xffffff;

struct Node{
    int v; // 边的弧尾
    int weight; // u-v边权
};

vector<Node> Adj[550]; // 邻接表(保存邻接边的权重(城市之间距离))
vector<int> V(550);    // 顶点表(保存点的权重(城市队伍数量))

vector<int> pathnum(550); // path[C2]为源点C1到终点C2的最短路径的数量
vector<int> dist(550);    // dist[C2]为源点C1到终点C2的最短路径长度
vector<int> team(550);    // team[C2]为在最快从C1到达C2的条件下,从源点C1出发到达C2能找到最多救援队数量
vector<int> vis(550);     // 标记访问

void Dijkstra(int s){ // s是源点
    fill(begin(pathnum), end(pathnum), 0);
    fill(begin(dist), dist.end(), INF);
    fill(begin(team), end(team), 0);
    fill(begin(vis), end(vis), false);
    dist[s] = 0;
    team[s] = V[s];
    pathnum[s] = 1;
    
    for(int i = 0;i<N;++i){
    	// 寻找未访问顶点中和源点最短路径最小的那个顶点
        int u = -1;
        int MIN = INF;        
        for(int j = 0;j<N;++j){
            if(vis[j] == false && dist[j] < MIN){
                u = j;
                MIN = dist[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 && dist[u] + Adj[u][j].weight < dist[v]){
                dist[v] = dist[u] + Adj[u][j].weight;
                team[v] = team[u] + V[v];
                pathnum[v] = pathnum[u];
            }
            else
            if(vis[v] == false && dist[u] + Adj[u][j].weight == dist[v]){
            	/* 在多条最短路径中选择队伍最多(点权和最大)的一种(
            	也就是说team[v]和dist[v]可能不是同一条最短路径上的数据 */
                team[v] = max(team[u] + V[v], team[v]); 
                pathnum[v] = pathnum[v] + pathnum[u];
            }
            
        }
    }
}

int main() {
    cin >> N >> M >> C1 >> C2;
    for(int i = 0;i<N;++i)
        cin >> V[i];
    for(int i = 0;i<M;++i){
        int c1, c2, length;
        cin >> c1 >> c2 >> length;
        Adj[c1].push_back({c2, length});
        Adj[c2].push_back({c1, length}); // 记住是双向边,不然会导致测试点2、5错误
        
    }
    Dijkstra(C1);
    cout << pathnum[C2] << " " << team[C2];
    
    return 0;
}

二刷 Dijkstra + DFS 邻接表

# include <iostream>
# include <vector>
# include <algorithm>

using namespace std;

int N;  // 顶点数
int M;  // 边数
int C1; // 源点
int C2; // 终点

const int INF = 0xffffff;
const int MAXV = 550;

struct Node{
    int v; // 边的弧尾
    int weight; // u-v边权
};

vector<Node> Adj[MAXV]; // 邻接表(保存邻接边的权重(城市之间距离))
vector<int> V(MAXV);  // 顶点表(保存点的权重(城市队伍数量))
vector<int> dist(MAXV); // dist[C2]为源点C1到终点C2的最短路径长度
vector<int> vis(MAXV);  // 标记访问

vector<int> pre[MAXV];
void Dijkstra(int s){
    fill(begin(dist), dist.end(), INF);
    fill(begin(vis), end(vis), false);
    dist[s] = 0;
    
    for(int i = 0;i<N;++i){
        int u = -1;
        int MIN = INF;        
        for(int j = 0;j<N;++j){
            if(vis[j] == false && dist[j] < MIN){
                u = j;
                MIN = dist[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 && dist[u] + Adj[u][j].weight < dist[v]){
                dist[v] = dist[u] + Adj[u][j].weight;
                pre[v].clear();
                pre[v].push_back(u);
            }
            else
            if(vis[v] == false && dist[u] + Adj[u][j].weight == dist[v]){
                pre[v].push_back(u);
            }
            
        }
    }
}
int maxPathNum = 0; // 最大最短路径数
int maxTeamNum; // 第二标尺最优值
vector<int> path, temppath; // 根据第二标尺获得:最优路径、临时路径
void DFS(int v){
    // 递归边界(死胡同)
    if(v == C1){ 
        // 因为递归边界是叶子结点,也就是从终点到达起点的次数,
        // 也就是最短路径的条数,所以每次就自增就可得到最短路径的条数
        maxPathNum++; 
        temppath.push_back(C1);
        
        // 计算临时第二标尺值
        int teamnum = 0;
        for(int t: temppath){
            teamnum += V[t]; // 记住t是最短路径上的顶点索引,teamnum加的是顶点权重V[t]
        }
        // 如果临时第二标尺值比已知最大值大,那就更新全局第二标尺值
        if(teamnum > maxTeamNum){
            maxTeamNum = teamnum;
            path = temppath;
        }
        temppath.pop_back();
        return;
    }
    
    temppath.push_back(v);
    // 岔路口(多个前驱结点,多条最短路径)
    for(int i = 0;i < pre[v].size();++i){
        DFS(pre[v][i]);
    }
    temppath.pop_back();
}

int main() {
    cin >> N >> M >> C1 >> C2;
    for(int i = 0;i<N;++i)
        cin >> V[i];
    for(int i = 0;i<M;++i){
        int c1, c2, length;
        cin >> c1 >> c2 >> length;
        Adj[c1].push_back({c2, length});
        Adj[c2].push_back({c1, length}); // 记住是双向边,不然会导致测试点2、5错误
        
    }
    Dijkstra(C1);
    DFS(C2);
    cout << maxPathNum << " " << maxTeamNum;
    
    return 0;
}

过段时间又写了一遍

#include<bits/stdc++.h>
using namespace std;

const int INF = 0xffffff;
int N, M, C1, C2;

struct Node{
    int v, weight;
};
int dist[510];
bool visit[510] = {false};
int team[510];
vector<Node> G[510];
vector<int> pre[510];

void Dijkstra(int s){
    fill(dist, end(dist), INF);
    dist[s] = 0;

    for(int i = 0;i < N;++i) {
        int u = -1;
        int mindist = INF;
        for(int j = 0;j < N;j++){
            if(visit[j] == false && dist[j] < mindist) {
                u = j;
                mindist = dist[j];
            }
        }
        if(u == -1) return;
        
        visit[u] = true;
        for(Node node: G[u]) {
            int v = node.v, u_v = node.weight;
            if(visit[v] == false && dist[u] + u_v < dist[v]) {
                dist[v] = dist[u] + u_v;
                pre[v].clear();
                pre[v].push_back(u);
            } else 
            if(visit[v] == false && dist[u] + u_v == dist[v]) {
                pre[v].push_back(u);
            }
        }
    }
}

vector<int> tempPath;
int maxTeam = 0, pathNum = 0;
void DFS(int u) {
    if(u == C1){
        tempPath.push_back(u);    
            
        pathNum++;
        int tempTeam = 0;
        for(int i: tempPath)
            tempTeam += team[i];
        maxTeam = min(maxTeam, tempTeam);

        tempPath.pop_back();
        return;
    }

    tempPath.push_back(u);
    for(int v: pre[u])
        DFS(v);
    tempPath.pop_back();
}

int main() {
    cin >> N >> M >> C1 >> C2;
    for(int i = 0;i < N;++i)
        cin >> team[i];
    for(int i = 0;i < M;++i){
        int u, v, len;
        cin >> u >> v >> len;
        G[u].push_back({v, len});
        G[v].push_back({u, len});
    }
    Dijkstra(C1);
    DFS(C2);
    cout << pathNum << " " << maxTeam << endl;

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值