Code Practice Journal | Day59-60_Graph09 最短路径(待更)

1. Dijkstra

1.1 原理与步骤

步骤:

  1. 选取距离源点最近且未被访问过的节点
  2. 标记该节点为已访问
  3. 更新未访问节点到源点的距离
1.2 代码实现

以KamaCoder47题为例
题目:47. 参加科学大会(第六期模拟笔试) (kamacoder.com)

class Program
{
    public static void Main(string[] args)
    {
        //处理输入
        string[] dimensions = Console.ReadLine().Split();
        int n = int.Parse(dimensions[0]);
        int m = int.Parse(dimensions[1]);
        //  visited & minDist & graph
        bool[] visited = new bool[n + 1];
        int[] minDist = new int[n + 1];
        int[][] graph = new int[n + 1][];
        for (int i = 0; i <= n; i++)
        {
            graph[i] = new int[n + 1];
            for (int j = 0; j <= n; j++)
            {
                graph[i][j] = int.MaxValue;
            }
        }
        //  填充
        for (int i = 0; i <= n; i++)
        {
            visited[i] = false;
            if (i == 1) minDist[i] = 0;
            else minDist[i] = int.MaxValue;
        }
        for (int i = 0; i < m; i++)
        {
            string[] edges = Console.ReadLine().Split();
            int start = int.Parse(edges[0]);
            int end = int.Parse(edges[1]);
            int weight = int.Parse(edges[2]);
            graph[start][end] = weight;
        }
        //
        int result = Dj(graph, n, visited, minDist);
        Console.WriteLine(result);
    }

    public static int Dj(int[][] graph, int n, bool[] visited, int[] minDist)
    {
        for (int count = 1; count < n + 1; count++)
        {
            //find min node
            int mindist = int.MaxValue;
            int minnode = 0;
            for (int i = 1; i < n + 1; i++)
            {
                if (visited[i] == false)
                {
                    if (minDist[i] < mindist)
                    {
                        minnode = i;
                        mindist = minDist[i];
                    }
                }
            }
            //update visited
            visited[minnode] = true;
            //update minDist
            for (int i = 1; i < n + 1; i++)
            {
                if (graph[minnode][i] != int.MaxValue)
                {
                    minDist[i] = Math.Min(graph[minnode][i] + mindist, minDist[i]);
                }
            }
            Console.WriteLine(string.Join(" ", minDist));
        }

        return minDist[n] == int.MaxValue ? -1 : minDist[n];
    }
}
1.3 堆优化

1、使用 List<Edge>[] 表示图

Edge结构体

public struct Edge
{
    public int To;    // 邻接顶点
    public int Val;   // 边的权重

    public Edge(int to, int val)
    {
        To = to;
        Val = val;
    }
}

图的表示

List<Edge>[] graph = new List<Edge>[V + 1];
for (int i = 1; i <= V; i++)
{
    graph[i] = new List<Edge>();
}

2、输入处理

3、初始化

int[] minDist = new int[V + 1];
for (int i = 0; i <= V; i++)
{
    minDist[i] = int.MaxValue;
}
minDist[start] = 0;

bool[] visited = new bool[V + 1];

PriorityQueue<int, int> pq = new PriorityQueue<int, int>();
pq.Enqueue(start, 0); // 起点入队,距离为0

4、算法主循环

while (pq.Count > 0)
{
    // 从优先队列中取出距离最小的节点
    pq.TryDequeue(out int currentNode, out int currentDist);

    // 如果已经访问过,跳过
    if (visited[currentNode])
        continue;

    // 标记为已访问
    visited[currentNode] = true;

    // 遍历所有邻接点
    foreach (var edge in graph[currentNode])
    {
        int neighbor = edge.To;
        int weight = edge.Val;

        // 如果未访问且可以通过当前节点更新最短距离
        if (!visited[neighbor] && currentDist + weight < minDist[neighbor])
        {
            minDist[neighbor] = currentDist + weight;
            pq.Enqueue(neighbor, minDist[neighbor]);
        }
    }
}

5、输出

完整代码:

namespace DijkstraAlgorithm
{
    // 定义一个结构体来表示带权重的边
    public struct Edge
    {
        public int To;    // 邻接顶点
        public int Val;   // 边的权重

        public Edge(int to, int val)
        {
            To = to;
            Val = val;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            // 读取顶点数V和边数E
            string[] firstLine = Console.ReadLine().Split();
            int V = int.Parse(firstLine[0]);
            int E = int.Parse(firstLine[1]);

            // 初始化邻接表
            List<Edge>[] graph = new List<Edge>[V + 1];
            for (int i = 1; i <= V; i++)
            {
                graph[i] = new List<Edge>();
            }

            // 读取所有边的信息
            for (int i = 0; i < E; i++)
            {
                string[] edgeInput = Console.ReadLine().Split();
                int p1 = int.Parse(edgeInput[0]);
                int p2 = int.Parse(edgeInput[1]);
                int val = int.Parse(edgeInput[2]);

                // 添加边到邻接表(无向图需要双向添加)
                graph[p1].Add(new Edge(p2, val));
                graph[p2].Add(new Edge(p1, val));
            }

            int start = 1;  // 起点
            int end = V;    // 终点

            // 存储从源点到每个节点的最短距离
            int[] minDist = new int[V + 1];
            for (int i = 0; i <= V; i++)
            {
                minDist[i] = int.MaxValue;
            }
            minDist[start] = 0;

            // 记录顶点是否被访问过
            bool[] visited = new bool[V + 1];

            // 优先队列中存放节点,优先级是源点到该节点的距离
            // 使用C#内置的PriorityQueue,参数为元素类型和优先级类型
            PriorityQueue<int, int> pq = new PriorityQueue<int, int>();
            pq.Enqueue(start, 0); // 起点入队,距离为0

            while (pq.Count > 0)
            {
                // 从优先队列中取出距离最小的节点
                pq.TryDequeue(out int currentNode, out int currentDist);

                // 如果已经访问过,跳过
                if (visited[currentNode])
                    continue;

                // 标记为已访问
                visited[currentNode] = true;

                // 遍历所有邻接点
                foreach (var edge in graph[currentNode])
                {
                    int neighbor = edge.To;
                    int weight = edge.Val;

                    // 如果未访问且可以通过当前节点更新最短距离
                    if (!visited[neighbor] && currentDist + weight < minDist[neighbor])
                    {
                        minDist[neighbor] = currentDist + weight;
                        pq.Enqueue(neighbor, minDist[neighbor]);
                    }
                }
            }

            // 输出结果
            if (minDist[end] == int.MaxValue)
                Console.WriteLine(-1); // 不能到达终点
            else
                Console.WriteLine(minDist[end]); // 到达终点的最短路径
        }
    }
}

2. Bellman_ford

2.1 原理与步骤
2.2 代码实现

以KamaCoder 94题为例
题目:
94. 城市间货物运输 I (kamacoder.com)

class Program
{
    public static void Main(string[] args)
    {
        //处理输入
        string[] dimensions = Console.ReadLine().Split();
        int n = int.Parse(dimensions[0]);
        int m = int.Parse(dimensions[1]);
        //minDist
        int[] minDist = new int[n + 1];
        for (int i = 0; i <= n; i++)
        {
            minDist[i] = i == 1 ? 0 : int.MaxValue;
        }
        //edges
        int[][] edges = new int[m][];
        for (int i = 0; i < m; i++)
        {
            edges[i] = new int[3];
            string[] edge = Console.ReadLine().Split();
            edges[i][0] = int.Parse(edge[0]);
            edges[i][1] = int.Parse(edge[1]);
            edges[i][2] = int.Parse(edge[2]);
        }

        //BF
        if (BF(edges, minDist, n - 1))
        {
            Console.WriteLine(minDist[n]);
        }
        else
        {
            Console.WriteLine("unconnected");
        }
    }

    public static bool BF(int[][] edges, int[] minDist, int n)
    {
        bool isUpdate = true;
        int count = 1;
        while (count <= n && isUpdate == true)
        {
            count++;
            isUpdate = false;

            for (int i = 0; i < edges.Length; i++)
            {
                int start = edges[i][0];
                int end = edges[i][1];
                int weight = edges[i][2];
                if (minDist[start] != int.MaxValue)
                {
                    int dist = minDist[start] + weight;
                    if (dist < minDist[end])
                    {
                        minDist[end] = dist;
                        isUpdate = true;
                    }
                }
            }
        }

        return !isUpdate;
    }
}
2.3 队列优化
class Edge
{
    public int To { get; set; }
    public int Weight { get; set; }

    public Edge(int to, int weight)
    {
        To = to;
        Weight = weight;
    }
}

class Program
{
    public static void Main(string[] args)
    {
        string[] input = Console.ReadLine().Split();
        int n = int.Parse(input[0]);
        int m = int.Parse(input[1]);

        List<Edge>[] graph = new List<Edge>[n + 1];
        for (int i = 1; i <= n; i++)
        {
            graph[i] = new List<Edge>();
        }

        bool[] isInQueue = new bool[n + 1];

        for (int i = 0; i < m; i++)
        {
            string[] edgeInput = Console.ReadLine().Split();
            int p1 = int.Parse(edgeInput[0]);
            int p2 = int.Parse(edgeInput[1]);
            int val = int.Parse(edgeInput[2]);

            graph[p1].Add(new Edge(p2, val));
        }

        int start = 1; // 起点
        int end = n;   // 终点

        int[] minDist = new int[n + 1];
        for (int i = 1; i <= n; i++)
        {
            minDist[i] = int.MaxValue;
        }
        minDist[start] = 0;

        Queue<int> queue = new Queue<int>();
        queue.Enqueue(start);
        isInQueue[start] = true;

        while (queue.Count > 0)
        {
            int node = queue.Dequeue();
            isInQueue[node] = false;

            foreach (var edge in graph[node])
            {
                int from = node;
                int to = edge.To;
                int weight = edge.Weight;

                if (minDist[to] > minDist[from] + weight)
                {
                    minDist[to] = minDist[from] + weight;
                    if (!isInQueue[to])
                    {
                        queue.Enqueue(to);
                        isInQueue[to] = true;
                    }
                }
            }
        }

        if (minDist[end] == int.MaxValue)
        {
            Console.WriteLine("unconnected");
        }
        else
        {
            Console.WriteLine(minDist[end]);
        }
    }
}
2.4 应用场景

3. Floyd

3.1 原理与步骤

动态规划

1、动规数组

grid[ i ][ j ][ k ]:节点 i 到节点 j ,以[ 1 ... k]为中间节点的最短距离

2、递推公式

case1:经过节点 k:grid[ i ][ j ][ k ] = grid[ i ][ k ][ k-1] + grid[ k ][ j ][ k-1 ]
case2:不经过节点 k:grid[ i ][ j ][ k ] = grid[ i ][ j ][ k-1]
取最小值

3、初始化

对于图中存在的每一条双向边,其距离为边的两节点无中间节点的最短距离,即grid[ i ][ j ][ 0 ] = m
对于图中不存在边的两个节点,其距离被初始化为整数最大值,即grid[ i ][ j ][ 0 ] = int.MaxValue

4、遍历顺序

由遍历公式可以看出,遍历顺序应该为 k 值从小到大,而对 i 和 j 无固定要求

3.2 代码实现

以KamaCoder 97题为例
题目:97. 小明逛公园 (kamacoder.com)

class Program
{
    public static void Main(string[] args)
    {
        //处理输入&初始化
        string[] dimensions = Console.ReadLine().Split();
        int v = int.Parse(dimensions[0]);
        int e = int.Parse(dimensions[1]);
        int[,,] grid = new int[v + 1, v + 1, v + 1];
        for (int i = 0; i <= v; i++)
        {
            for (int j = 0; j <= v; j++)
            {
                for (int k = 0; k <= v; k++)
                {
                    grid[i, j, k] = int.MaxValue;
                }
            }
        }
        for (int i = 0; i < e; i++)
        {
            string[] edge = Console.ReadLine().Split();
            int n1 = int.Parse(edge[0]);
            int n2 = int.Parse(edge[1]);
            int w = int.Parse(edge[2]);
            grid[n1, n2, 0] = w;
            grid[n2, n1, 0] = w;
        }
        //输出列表
        int count = int.Parse(Console.ReadLine());
        int[][] outresult = new int[count][];
        for (int i = 0; i < count; i++)
        {
            outresult[i] = new int[2];
            string[] nodes = Console.ReadLine().Split();
            outresult[i][0] = int.Parse(nodes[0]);
            outresult[i][1] = int.Parse(nodes[1]);
        }

        //递推
        for (int k = 1; k <= v; k++)
        {
            for (int i = 1; i <= v; i++)
            {
                for (int j = 1; j <= v; j++)
                {
                    int c1, c2;
                    if (grid[i, k, k - 1] != int.MaxValue && grid[k, j, k - 1] != int.MaxValue) c1 = grid[i, k, k - 1] + grid[k, j, k - 1];
                    else c1 = int.MaxValue;
                    c2 = grid[i, j, k - 1];
                    grid[i, j, k] = Math.Min(c1, c2);
                }
            }
        }

        //输出
        for (int i = 0; i < count; i++)
        {
            int result = grid[outresult[i][0], outresult[i][1], v];
            Console.WriteLine(result == int.MaxValue ? -1 : result);
        }
    }
}

4. A*

4.1 原理与步骤
4.2 代码实现

5. 总结

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值