最短路径前两种算法实现 例题 sdut oj2143

本文介绍了图结构中最短路径问题的两种经典算法——弗洛伊德算法和迪杰斯特拉算法,并提供了这两种算法的详细解释及C++实现代码。

图结构练习——最短路径

Time Limit: 1000MS Memory limit: 65536K

题目描述

 给定一个带权无向图,求节点1到节点n的最短路径。
 

输入

 输入包含多组数据,格式如下。
第一行包括两个整数n m,代表节点个数和边的个数。(n<=100)
剩下m行每行3个正整数a b c,代表节点a和节点b之间有一条边,权值为c。
 

输出

 每组输出占一行,仅输出从1到n的最短路径权值。(保证最短路径存在)
 

示例输入

3 2
1 2 1
1 3 1
1 0

示例输出

1
0

提示

 两种算法,弗洛伊德时间复杂度为O(N^3)
                迪杰斯特拉时间复杂度为O(N^2)

示例程序


算法一:
   弗洛伊德算法:Floyed-Warshall
根据经验,如果要让任意两个顶点之间的路程变短,只能引入
第三个点通过这个点中转求得。
假如中转点是顶点K,只需判断mp[i][k]+mp[k][j]是否比mp[i][j]小即可。
基本思想是:最开始只允许经过一个顶点进行中转,接下来++i实现
从i号顶点到j号顶点只经过前k号点的最短路径。
Floyd算法完整代码:


#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;

int main()
{
    int n, m;
    int mp[112][112];
    while(cin >> n >> m)
    {
        for(int i = 1; i <= n; i++)
        {
            for(int j = 1; j <= n; j++)
            {
                if(i == j)
                    mp[i][j] = 0;
                else
                    mp[i][j] = INF;
            }
        }
        while(m--)
        {
            int a, b, c;
            cin >> a >> b >> c;
            if(mp[a][b] > c)
                mp[a][b] = mp[b][a] = c;
        }
        Floyd  弗洛伊德算法核心语句:
        for(int i = 1; i <= n; i++)
        {
            for(int j = 1; j <= n; j++)
            {
                if(mp[i][j] != INF)
                {
                    for(int k = 1; k <= n; k++)
                    {
                        mp[j][k] = min(mp[j][k], mp[j][i]+mp[i][k]);
                    }
                }
            }
        }
//        for(int k = 1; k <= n; k++)
//        {
//            for(int i = 1; i <= n; i++)
//            {
//                for(int j = 1; j <= n; j++)
//                {
//                    if(mp[i][k] < INF&&mp[k][j] < INF&&mp[i][j] > mp[i][j]+mp[k][j])
//                        mp[i][j] = mp[i][k]+mp[k][j];
//                }
//            }
//        }
        cout << mp[1][n] << endl;
    }
    return 0;
}

算法二:
迪杰斯特拉 Dijkstra -- 单源最短路
此算法的基本思想是:每次找到离源点最近的顶点,然后以该顶点为
            中心进行扩展,(松弛)最终得到源点到其余所有点的
            最短路径。
此算法基本步骤如下:
1:
   将所有的顶点分为两部分:已知最短路程的顶点集合P和未知最短
路径的顶点集合Q。最开始,集合P中只有源点一个顶点。我们这里用
一个book数组来记录那些点在集合P中。例如对于某个顶点i,如果
book[i]为1则表示这个顶点在集合P中,如果book[i]为0则表示这个顶
点在集合Q中。
2:
    设置源点s到自己的最短路径为0即dis[s]=0。若存在有源点能直
接到达的顶点i,则把dis[i]设为mp[s][i]。同时把所有其他(源点不
能直接到达的)顶点的最短路径设为无穷。
3:
    在集合Q的所有顶点中选择一个离源点s最近的顶点u(即dis[u]最小)
加入的集合P。并考察所有以点u为起点的边,对每一条边进行松弛操作。
例如存在一条从u到v的边,那么可以通过将边u->v添加到尾部来拓展一
条从s到v的路径,这条路径的长度是dis[u]+mp[u][v]。如果这个值比目
前一致的dis[v]的值要小,我们可以用新值来替代当前dis[v]中的值。
4:
    重复第3步,如果集合Q为空,算法结束。最终dis数组中的值就是
源点到所有定点的最短路径。


完整的Dijkstra算法代码:

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f

        using namespace std;

int mp[110][110];
int book[110], dis[110];
int Min, u;
int main()
{
    int m, n;
    while(cin >> n >> m)
    {
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= n; j++)
            {
                if(i == j)
                    mp[i][j] = 0;
                else
                    mp[i][j] = INF;
            }
        while(m--)
        {
            int a, b, c;
            scanf("%d %d %d", &a, &b, &c);
            if(mp[a][b] > c)
            {
                mp[a][b] = mp[b][a] = c;
            }
        }
        for(int i = 1; i <= n; i++)
            dis[i] = mp[1][i];
        for(int i = 1; i <= n; i++)
            book[i] = 0;
        book[1] = 1;
        Dijkstra算法核心语句:
        for(int i = 1; i <= n-1; i++)
        {
            Min = INF;
            for(int j = 1; j <= n; j++)
            {
                if(!book[j]&&dis[j] < Min)
                {
                    Min = dis[j];
                    u = j;
                }
            }
            book[u] = 1;
            for(int v = 1; v <= n; v++)
            {
                if(mp[u][v] < INF)
                {
                    dis[v] = min(dis[v], dis[u]+mp[u][v]);
                }
            }
        }
        printf("%d\n", dis[n]);
    }
    return 0;
}



### 关于 SDUTOJ 平台上离散数学中群的判定 #### 背景介绍 在离散数学中,“群”的定义是一个代数结构,它由一个集合 \( G \) 和该集合上的二元运算 \( * \) 组成,并满足四个基本性质:封闭性、结合律、单位元的存在性和逆元的存在性[^1]。 对于 SDUTOJ 上涉及“群的判定”类问题,通常会提供一组数据来验证这些条件是否成立。这类问题可以通过编程实现自动化判断过程。下面详细介绍如何通过算法设计完成此类任务。 --- #### 封闭性检验 给定集合 \( S=\{a_1,a_2,...,a_n\} \),以及对应的乘法表(或操作表),需确认任意两个元素相乘的结果仍属于原集合\(S\)。 ```python def check_closure(operation_table): n = len(operation_table) for i in range(n): for j in range(n): result_index = operation_table[i][j] if not (0 <= result_index < n): return False # 如果结果不在有效索引范围内,则违反封闭性 return True ``` 此函数遍历整个操作表并检查每一对输入对应的结果是否仍然位于合法范围之内[^2]。 --- #### 单位元查找与验证 如果存在某个特定元素e使得对于所有的g∈G都有 g*e=e*g=g 成立的话,e就是这个群里的唯一单位元. ```python def find_identity_element(operation_table): n = len(operation_table) possible_identities = [] for candidate_e in range(n): is_valid = True # Check left identity property: e * a_i == a_i for row_idx in range(n): if operation_table[candidate_e][row_idx] != row_idx: is_valid = False # Also need to verify right side since we want two-sided identities here if is_valid: for col_idx in range(n): if operation_table[col_idx][candidate_e] != col_idx: is_valid = False if is_valid: possible_identities.append(candidate_e) return None if len(possible_identities)!=1 else possible_identities[0] ``` 上述代码尝试找到可能存在的单位元,并确保它是唯一的两面单位元[^3]。 --- #### 结合律检测 要证明某组数据遵循结合律(a*(b*c)=(a*b)*c),理论上需要穷举所有可能性组合来进行测试.然而实际应用当中往往采用随机抽样方法近似代替全面覆盖. --- #### 逆元发现机制 一旦确定了有效的单位元之后就可以进一步去探索各个成员各自的倒置伙伴关系是否存在. ```python def has_inverses_for_all_elements(operation_table,identity): n=len(operation_table) inverses_found=[False]*n for elem_a in range(n): found=False for elem_b in range(n): product=operation_table[elem_a][elem_b] if product==identity: inverses_found[elem_a]=True break return all(inverses_found) ``` 这段脚本旨在逐一排查每一个成分能否寻得相应的反向关联项[^4]. --- ### 总结说明 综合以上几个方面的考量因素可以构建起完整的解决方案框架用于处理类似SDUTOJ平台提出的有关离散数学领域内的'群’概念判别挑战项目。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值