zoukankan      html  css  js  c++  java
  • 7-8 哈利·波特的考试,7-9 旅游规划,7-10 公路村村通

    之所以把 7-8,7-9,7-10 放到一起,是因为这三个题都是非常经典的图算法,在之前总结的算法笔记中已经提到过,这里只是做一个复习,所以整理到一起。

    发现自己的记忆力是真的不行,还是需要勤加努力多认真学习才能把这些算法都掌握住。

    7-9 哈利波特的考试

    这道题目是想考全局最短路径,Floyd 算法,直接使用 Floyd 算法即可。

    关于 Floyd 算法主要是用来求全局最短路径,我们这里给出一份 Floyd算法 的伪代码:

    枚举顶点 k 属于 [1, n]
      以顶点 k 作为中介点,枚举所有顶点对 i 和 j
      	如果 dis[i][k] + dis[k][j] < dis[i][j]
      		赋值 dis[i][j] = dis[i][k] + dis[k][j]
    

    但是个人觉得这道题目的难度在于读题 TAT,我读题读了半才读懂是什么意思,看来还是需要提高阅读能力。

    完整的解题过程如下:

    /*
        Author: Veeupup
        哈利波特的考试
     */
    #include <iostream>
    #include <cstdio>
    #include <cstdint>
    #include <climits>
    using namespace std;
    
    const int maxn = 110, INF = 1e5;
    
    int n, m;
    int dis[maxn][maxn];
    
    // 朴素的 floyd 算法,求全局最短路径
    void floyd() {
        for (int k = 1; k <= n; k++)
            for (int i = 1; i <= n; i++)
                for(int j = 1; j <= n; j++)
                    dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
    }
    
    int main()
    {
        freopen("data.txt","r", stdin);    
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= n; i++)
        {   // 初始化点之间的距离,自己到自己距离为 0
            for (int j = 1; j <= n; j++)
            {
                if(i == j) dis[i][j] = 0;
                else dis[i][j] = INF;
            }
        }
        int v1, v2, weight;
        for (int i = 1; i <= m; i++)
        {
            scanf("%d%d%d", &v1, &v2, &weight);
            dis[v1][v2] = dis[v2][v1] = weight;
        }
        
        floyd();
    
        int ans = INF;
        int cnt = 0;
        for (int i = 1; i <= n; i++)
        {
            int Max = 0;
            for (int j = 1; j <= n; j++)
            {
                if(dis[i][j] == INF) 
                {   // 如果有孤立的结点,那么就无法完成变形
                    printf("0");
                    return 0;
                }
                Max = max(Max, dis[i][j]);
            }
            if(Max < ans) 
            {
                ans = Max;
                cnt = i;
            }
        }
        printf("%d %d", cnt, ans);
        return 0;
    }
    

    7-8 旅游规划

    这道题是很经典的具有多个标尺的单源最短路径的变形,我们使用 Dijkstra + DFS 来实现多个标尺的计算,不用在 Dijkstra 中将原算法修改的很复杂。

    关于 Dijkstra + DFS 可以参考这里的文章来进行复习。

    下面给出完整代码实现:

    /*
        Author: Veeupup
        旅游规划
        单源最短路径 Dijkstra,有两个标尺 1. 距离 2. 过路费
    
        典型的单源最短路径的变形
    
     */
    #include <iostream>
    #include <cstdio>
    #include <cstdint>
    #include <vector>
    using namespace std;
    
    const int maxn = 510, INF = 1e4;
    
    int N, M, S, D;
    int G[maxn][maxn];  // 保存距离,第一标尺
    int cost[maxn][maxn]; // 保存收费金额,图的边权
    int dis[maxn], minCost = INF;  // 记录起点到某点的最小距离
    bool vis[maxn] = {false};   // 记录是否访问过
    
    vector<int> pre[maxn];  // 保存前一个结点
    vector<int> tempPath, path;
    
    void Initial()
    {   // 初始化距离和花费最大
        fill(G[0], G[0] + maxn * maxn, INF);
        fill(cost[0], cost[0] + maxn * maxn, INF);
    }
    
    void Dijkstra(int S) {
        fill(dis, dis+maxn, INF);   // 初始化到所有点距离无限大
        dis[S] = 0; // 到自己距离为 0
        for (int i = 0; i < N; i++)
        {
            int u = -1, MIN = INF;
            for (int j = 0; j < N; j++)
            {   // 寻找未访问的距离最小值
                if(vis[j] == false && dis[j] < MIN) {
                    u = j;
                    MIN = dis[j];
                }
            }
            if(u == -1) break;  // 所有可以到达的点都访问过了
            vis[u] = true;  // 设置为已访问
            for (int v = 0; v < N; v++)
            {   // 遍历能从 u 出发到达的所有点
                // 如果经过 u 到达 v 更近,那么就更新 v
                if(dis[u] + G[u][v] < dis[v]) {
                    dis[v] = dis[u] + G[u][v];
                    pre[v].clear();
                    pre[v].push_back(u);    // 经过 u 能够更近的到达 v
                }else if(dis[u] + G[u][v] == dis[v]) {
                    // 如果经过 u 到达的时候距离一样远,那么增加到其前面去
                    pre[v].push_back(u);
                }   
            }
        }
    }
    
    void DFS(int V) {
        if(V == S) {
            tempPath.push_back(S);
            int tempCost = 0;
            int nowId, nextId;
            for (int i = tempPath.size()-1; i > 0; i--)
            {
                nowId = tempPath[i];
                nextId = tempPath[i-1];
                tempCost += cost[nowId][nextId];
            }
            if(tempCost < minCost) {
                minCost = tempCost;
                path = tempPath;
            }
            tempPath.pop_back();
            return;
        }else {
            tempPath.push_back(V);
            for (int i = 0; i < pre[V].size(); i++)
            {
                DFS(pre[V][i]);
            }
            tempPath.pop_back();
        }
    }
    
    int main()
    {
        freopen("data.txt","r", stdin);
        Initial();
        scanf("%d%d%d%d", &N, &M, &S, &D);
        int u, v;
        for (int i = 0; i < M; i++)
        {   // 读取图信息
            scanf("%d%d", &u, &v);
            scanf("%d%d", &G[u][v], &cost[u][v]);
            G[v][u] = G[u][v];
            cost[v][u] = cost[u][v];
        }
        Dijkstra(S);
        DFS(D);   // 从终点向前回溯
        printf("%d %d
    ", dis[D], minCost);
    
        return 0;
    
    }
    

    7-10 公路村村通

    这道题是非常经典的最小生成树算法,推荐使用kruskal算法。

    这个题没什么特殊的地方,掌握好最小生成树算法即可,又复习了一遍图算法吧。

    完整代码如下:

    /*
        Author: Veeupup
        公路村村通
    
        最小生成树算法:
        Prim(类似Dijkstra) + Krusktal(边贪心)
    
        这里选择使用 Krusktal 算法来实现
        算法思想很简单
    
     */
    #include <iostream>
    #include <cstdio>
    #include <cstdint>
    #include <algorithm>
    using namespace std;
    
    const int maxn = 1010, INF = INT32_MAX;
    
    int N, M;
    struct edge
    {
        int u, v;
        int cost; // 边权
    } E[maxn * 3];
    
    bool cmp(edge a, edge b)
    {
        return a.cost < b.cost;
    }
    
    // 使用并查集
    int father[maxn];
    int findFather(int x)
    {
        while (x != father[x])
        {
            x = father[x];
        }
        return x;
    }
    
    // n 为顶点个数, m 为图的边数
    int kruskal(int n, int m)
    {
        // ans 为所求边权之和
        int ans = 0, Num_edge = 0;
        for (int i = 1; i <= n; i++)
        {
            father[i] = i;
        } // 并查集初始化
        sort(E, E + m, cmp);
        for (int i = 0; i < m; i++)
        {
            int faU = findFather(E[i].u);
            int faV = findFather(E[i].v);
            if (faU != faV)
            {
                father[faU] = faV;
                ans += E[i].cost;
                Num_edge++;
                if (Num_edge == n - 1)
                    break;
            }
        }
        if (Num_edge != n - 1)
            return -1;
        return ans;
    }
    
    int main()
    {
        freopen("data.txt", "r", stdin);
        scanf("%d%d", &N, &M);
        int u, v, w;
        for (int i = 0; i < M; i++)
        {
            scanf("%d%d%d", &E[i].u, &E[i].v, &E[i].cost);
        }
        int ans = kruskal(N, M);
        printf("%d", ans);
        return 0;
    }
    

    希望对大家有帮助。

  • 相关阅读:
    在Spring中使用cache(EhCache的对象缓存和页面缓存)
    halcon 模板匹配 -- 转化 vector_angle_to_rigid
    halcon 模板匹配 -- find_shape_model
    halcon 模板匹配 -- create_shape_model
    C#快速获取指定网页源码的几种方式,并通过字符串截取函数 或 正则 取指定内容(IP)
    C# Socket通讯 本机多网卡,指定网卡通讯
    C# 获取所有网卡信息
    C#关闭退出线程的几种方法
    C#多线程方法 可传参
    C# Datetime 使用详解
  • 原文地址:https://www.cnblogs.com/veeupup/p/12599338.html
Copyright © 2011-2022 走看看