作者: admin2025

  • Minimum Window Substring,LeetCode,76

    题目描述

    给定一个字符串 S 和一个字符串 T,请在字符串 S 中找出包含 T 中所有字符的最小子串。

    示例

    输入: S = "ADOBECODEBANC", T = "ABC" 输出: "BANC"

    说明

    • 如果 S 中不存这样的子串,则返回空字符串 ""
    • 如果存在多个满足条件的最小子串,返回任意一个即可。

    解题思路

    这个问题可以通过滑动窗口(Sliding Window)的方法来解决。滑动窗口是一种常用的双指针技巧,适用于一维数组或字符串等线性结构。

    具体步骤

    1. 初始化变量
      • 使用两个指针 leftright 表示滑动窗口的左右边界,初始都指向 S 的起始位置。
      • 使用一个字典 need 来记录字符串 T 中每个字符的需求量。
      • 使用一个字典 window 来记录当前窗口中每个字符的数量。
      • 使用一个变量 valid 来记录窗口中满足 T 中字符需求的字符数量。
    2. 扩展右边界
      • 移动 right 指针,将 S[right] 加入窗口,并更新 windowvalid
    3. 收缩左边界
      • 当窗口中的字符已经满足 T 的需求时,尝试收缩窗口,移动 left 指针,更新 windowvalid,并记录当前窗口的长度和起始位置。
    4. 更新最小窗口
      • 在收缩左边界的过程中,不断更新最小窗口的长度和起始位置。
    5. 返回结果
      • 根据记录的最小窗口长度和起始位置,返回最小窗口子串。

    代码实现

    def minWindow(s: str, t: str) -> str: need = {} window = {} for char in t: need[char] = need.get(char, 0) + 1

    left, right = 0, 0
    valid = 0
    start, length = 0, float('inf')
    
    while right < len(s):
        # 扩展右边界
        c = s[right]
        right += 1
        if c in need:
            window[c] = window.get(c, 0) + 1
            if window[c] == need[c]:
                valid += 1
    
        # 收缩左边界
        while valid == len(need):
            if right - left < length:
                start = left
                length = right - left
    
            d = s[left]
            left += 1
            if d in need:
                if window[d] == need[d]:
                    valid -= 1
                window[d] -= 1
    
    return "" if length == float('inf') else s[start:start + length]

    示例

    s = "ADOBECODEBANC" t = "ABC" print(minWindow(s, t)) # 输出: "BANC"

    复杂度分析

    • 时间复杂度:O(N),其中 N 是字符串 S 的长度。每个字符最多被访问两次(一次被 right 指针访问,一次被 left 指针访问)。
    • 空间复杂度:O(|T|),其中 |T| 是字符串 T 的长度。主要是用于存储 needwindow 字典。

    总结

    滑动窗口是一种高效解决字符串子串问题的方法,通过双指针的移动来维护一个动态的窗口,并在窗口满足条件时进行收缩,从而找到满足条件的最小子串。这个方法在很多类似的问题中都有广泛的应用。

  • Edit Distance,LeetCode,72

    题目描述:

    LeetCode 72题,编辑距离(Edit Distance),是一道经典的动态规划问题。题目要求计算将一个字符串转换为另一个字符串所需的最少编辑操作次数。允许的编辑操作包括:

    1. 插入一个字符
    2. 删除一个字符
    3. 替换一个字符

    题目示例:

    输入:word1 = "horse", word2 = "ros" 输出:3 解释: horse -> rorse (将 'h' 替换为 'r') rorse -> rose (删除 'r') rose -> ros (删除 'e')

    解题思路:

    我们可以使用动态规划(DP)来解决这个问题。定义一个二维数组 dp,其中 dp[i][j] 表示将 word1 的前 i 个字符转换为 word2 的前 j 个字符所需的最少编辑操作次数。

    状态转移方程:

    1. word1[i-1] == word2[j-1],不需要进行操作,dp[i][j] = dp[i-1][j-1]
    2. word1[i-1] != word2[j-1],需要进行操作,取以下三种操作的最小值:
      • 插入操作:dp[i][j] = dp[i][j-1] + 1
      • 删除操作:dp[i][j] = dp[i-1][j] + 1
      • 替换操作:dp[i][j] = dp[i-1][j-1] + 1

    初始化:

    • dp[0][j] 表示将空字符串转换为 word2 的前 j 个字符,需要 j 次插入操作。
    • dp[i][0] 表示将 word1 的前 i 个字符转换为空字符串,需要 i 次删除操作。

    代码实现:

    def minDistance(word1, word2): m, n = len(word1), len(word2) dp = [[0] * (n + 1) for _ in range(m + 1)]

    # 初始化
    for i in range(m + 1):
        dp[i][0] = i
    for j in range(n + 1):
        dp[0][j] = j
    
    # 填充dp表
    for i in range(1, m + 1):
        for j in range(1, n + 1):
            if word1[i - 1] == word2[j - 1]:
                dp[i][j] = dp[i - 1][j - 1]
            else:
                dp[i][j] = min(dp[i - 1][j] + 1,  # 删除
                               dp[i][j - 1] + 1,  # 插入
                               dp[i - 1][j - 1] + 1)  # 替换
    
    return dp[m][n]

    示例

    word1 = "horse" word2 = "ros" print(minDistance(word1, word2)) # 输出:3

    复杂度分析:

    • 时间复杂度: O(m * n),其中 mn 分别是 word1word2 的长度。
    • 空间复杂度: O(m * n),用于存储 dp 表。

    优化空间复杂度:

    可以通过滚动数组的方式将空间复杂度优化到 O(min(m, n)),具体实现时只需使用一维数组即可。

    总结:

    编辑距离问题是一个典型的动态规划问题,通过定义合适的状态和状态转移方程,可以有效地求解。该问题在字符串处理、自然语言处理等领域有广泛的应用。掌握该问题的解法有助于理解和应用动态规划思想。

  • Climbing Stairs,LeetCode,70

    题目描述:

    你正在爬楼梯。需要 n 阶你才能到达楼顶。

    每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

    示例 1:

    输入:n = 2 输出:2 解释:有两种方法可以爬到楼顶。

    1. 1 阶 + 1 阶
    2. 2 阶

    示例 2:

    输入:n = 3 输出:3 解释:有三种方法可以爬到楼顶。

    1. 1 阶 + 1 阶 + 1 阶
    2. 1 阶 + 2 阶
    3. 2 阶 + 1 阶

    解题思路:

    这个问题实际上是一个斐波那契数列问题。假设 f(n) 表示爬到第 n 阶的方法数,那么有以下递推关系:

    • f(1) = 1,只有一种方法,即爬 1 阶。
    • f(2) = 2,有两种方法,即爬两个 1 阶或者一次爬 2 阶。
    • 对于 n > 2,可以从第 n-1 阶爬 1 阶到达第 n 阶,或者从第 n-2 阶爬 2 阶到达第 n 阶。因此,f(n) = f(n-1) + f(n-2)

    基于这个递推关系,可以使用动态规划的方法来求解。

    动态规划解法:

    1. 定义状态:dp[i] 表示爬到第 i 阶的方法数。
    2. 状态转移方程: dp[i] = dp[i-1] + dp[i-2]
    3. 初始状态: dp[1] = 1dp[2] = 2
    4. 返回结果: dp[n]

    代码实现(Python):

    class Solution: def climbStairs(self, n: int) -> int: if n == 1: return 1 if n == 2: return 2

        dp = [0] * (n + 1)
        dp[1] = 1
        dp[2] = 2
    
        for i in range(3, n + 1):
            dp[i] = dp[i - 1] + dp[i - 2]
    
        return dp[n]

    优化空间复杂度:

    由于每次状态转移只依赖于前两个状态,可以使用两个变量来存储这两个状态,从而将空间复杂度从 O(n) 优化到 O(1)。

    优化后的代码(Python):

    class Solution: def climbStairs(self, n: int) -> int: if n == 1: return 1 if n == 2: return 2

        a, b = 1, 2
        for i in range(3, n + 1):
            a, b = b, a + b
    
        return b

    总结:

    这个问题通过动态规划的方法可以高效解决。关键在于识别出问题的递推关系,并利用动态规划的思想进行状态转移。优化空间复杂度后,代码更加简洁且效率更高。

    希望这个详细的解答对你有所帮助!如果有其他问题,欢迎继续提问。

  • Maximum Subarray,LeetCode,53

    题目描述:

    给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

    示例:

    输入: [-2,1,-3,4,-1,2,1,-5,4], 输出: 6 解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。

    解题思路:

    这个问题可以使用动态规划来解决,经典的算法是Kadane算法。其核心思想是:

    1. 初始化两个变量,max_currentmax_globalmax_current 表示以当前元素结尾的最大子数组和,max_global 表示全局的最大子数组和。
    2. 遍历数组,对于每一个元素:
      • 更新 max_current 为当前元素和 max_current + 当前元素 中的较大值。
      • 更新 max_globalmax_globalmax_current 中的较大值。
    3. 最终,max_global 就是最大子数组的和。

    代码实现(Python):

    def maxSubArray(nums): if not nums: return 0

    max_current = max_global = nums[0]
    
    for i in range(1, len(nums)):
        max_current = max(nums[i], max_current + nums[i])
        max_global = max(max_global, max_current)
    
    return max_global

    示例

    nums = [-2,1,-3,4,-1,2,1,-5,4] print(maxSubArray(nums)) # 输出: 6

    详细解释:

    1. 初始化:
      • max_currentmax_global 都初始化为数组的第一个元素 nums[0]。这是因为子数组至少包含一个元素,所以初始的最大子数组和就是第一个元素本身。
    2. 遍历数组:
      • 从数组的第二个元素开始遍历(即索引为1的元素)。
      • 对于每一个元素 nums[i],计算以 nums[i] 结尾的最大子数组和 max_current。这个值是当前元素 nums[i]max_current + nums[i] 中的较大值。为什么要这样计算?因为如果 max_current 是负数,加上当前元素可能会使和变小,所以不如从当前元素重新开始计算子数组和。
      • 更新全局最大子数组和 max_global,这个值是 max_globalmax_current 中的较大值。
    3. 返回结果:
      • 遍历完数组后,max_global 就是最大子数组的和。

    复杂度分析:

    • 时间复杂度: O(n),其中 n 是数组的长度。只需要遍历一次数组。
    • 空间复杂度: O(1),只需要常数级别的额外空间。

    扩展:

    • 如果数组中所有元素都是负数,Kadane算法仍然适用,最终返回的是最大的负数。
    • 该算法可以扩展到二维数组,寻找最大子矩阵和的问题。

    希望这个详细的解释和代码实现能帮助你理解并解决LeetCode 53题 “Maximum Subarray”。如果有任何进一步的问题,欢迎继续提问!

  • Subsequence,POJ,3061

    题目 “Subsequence” 是一道经典的算法题目,通常出现在在线编程竞赛和算法练习中。POJ(Programming Online Judge)上的3061题就是这样一个问题。这道题目主要考察的是子序列和滑动窗口的相关算法。

    题目描述

    给定一个长度为 ( n ) 的整数序列 ( a_1, a_2, \ldots, a_n ) 和一个整数 ( S ),要求找到一个最短的连续子序列,使得这个子序列的和大于或等于 ( S )。如果不存在这样的子序列,输出 0。

    输入格式

    • 第一行包含一个整数 ( T ),表示测试数据的组数。
    • 每组测试数据包含两行:
      • 第一行包含两个整数 ( n ) 和 ( S )。
      • 第二行包含 ( n ) 个整数,表示序列 ( a_1, a_2, \ldots, a_n )。
  • Sightseeing trip,POJ,1734

    好的,您提到的“Sightseeing trip”是POJ(北京大学在线评测系统)上的一个编程题目,编号为1734。这个题目通常被称为“观光旅行”或“旅行路线优化”问题。下面我会详细解释这个题目的背景、要求以及常见的解题思路。

    题目背景

    题目描述的是一个旅行者想要参观若干个城市,每个城市之间有旅行费用。旅行者希望找到一条从起点城市出发,经过所有城市一次且仅一次,最后回到起点城市的路线,使得总旅行费用最小。这就是著名的旅行商问题(Traveling Salesman Problem, TSP)。

    题目要求

    1. 输入:第一行是城市数量 ( n )(( 2 \leq n \leq 20 )),接下来是一个 ( n \times n ) 的矩阵,表示城市之间的旅行费用。矩阵中的 ( a[i][j] ) 表示从城市 ( i ) 到城市 ( j ) 的费用。
    2. 输出:输出最小旅行费用的路线和总费用。

    解题思路

    由于题目中的城市数量 ( n ) 最大为20,可以使用动态规划加状态压缩的方法来求解。具体步骤如下:

    1. 状态表示
      • 用一个整数 ( state ) 表示当前已经访问过的城市集合,其中 ( state ) 的第 ( i ) 位为1表示已经访问过城市 ( i )。
      • 用 ( dp[state][i] ) 表示当前在 city ( i ),并且已经访问过集合 ( state ) 中的城市时的最小旅行费用。
    2. 状态转移
      • 初始状态:( dp[1][0] = 0 ),表示从城市0出发,初始费用为0。
      • 状态转移方程: [ dp[state][i] = \min_{j \in state \text{ 且 } j \neq i} { dp[state \text{ XOR } (1 << i)][j] + cost[j][i] } ] 其中 ( cost[j][i] ) 是从城市 ( j ) 到城市 ( i ) 的旅行费用。
    3. 求解结果
      • 最终结果为 ( dp[(1 << n) - 1][0] ),表示访问过所有城市并回到起点城市0的最小费用。

    代码示例(C++)

    #include #include #include #include using namespace std;

    const int MAXN = 20; int cost[MAXN][MAXN]; int dp[1 << MAXN][MAXN]; int n;

    int main() { cin >> n; for (int i = 0; i < n; ++i) { for (int j = 0; j < n; ++j) { cin >> cost[i][j]; } }

    memset(dp, 0x3f, sizeof(dp));
    dp[1][0] = 0;
    
    for (int state = 1; state < (1 << n); ++state) {
        for (int i = 0; i < n; ++i) {
            if (state & (1 << i)) {
                for (int j = 0; j < n; ++j) {
                    if (state & (1 << j) && i != j) {
                        dp[state][i] = min(dp[state][i], dp[state ^ (1 << i)][j] + cost[j][i]);
                    }
                }
            }
        }
    }
    
    cout << dp[(1 << n) - 1][0] << endl;
    
    return 0;

    }

    注意事项

    1. 状态压缩:使用位运算来表示和操作城市集合。
    2. 初始化:确保初始状态正确设置。
    3. 边界条件:注意处理城市间不可达的情况(通常用较大数值表示不可达)。

    通过上述方法和代码,可以有效地解决这个旅行商问题。希望这个详细的解释对您有所帮助!如果有更多问题,欢迎继续提问。

  • Highways,POJ,1751

    《Highways》是POJ(北京大学在线评测系统)上的一个经典图论问题,题目编号为1751。这个问题主要考察的是图的最小生成树(Minimum Spanning Tree, MST)算法,常见的解决方法是使用Kruskal算法或Prim算法。

    题目描述

    题目大意是:给定一些城市和它们之间的潜在高速公路建设成本,要求构建一个高速公路系统,使得所有城市都能互相连通,并且建设的总成本最小。

    输入

    • 第一行包含两个整数N和M,分别表示城市的数量和潜在高速公路的数量。
    • 接下来的M行,每行包含三个整数X, Y和C,表示在城市X和城市Y之间建设高速公路的成本为C。

    输出

    • 输出最小生成树的总成本。

    解决思路

    1. Kruskal算法
      • 将所有边按权重(成本)从小到大排序。
      • 初始化一个并查集,用于判断两个城市是否已经连通。
      • 遍历所有边,如果当前边的两个城市未连通,则将它们连接,并累加当前边的权重。
      • 最终累加的权重即为最小生成树的总成本。
    2. Prim算法
      • 选择一个起点城市,将其加入生成树集合。
      • 初始化一个优先队列(或最小堆),用于存储当前生成树集合到其他城市的最小边。
      • 不断从优先队列中取出最小边,如果该边连接的城市未在生成树集合中,则将其加入,并更新优先队列。
      • 最终累加的权重即为最小生成树的总成本。

    代码示例(Kruskal算法)

    #include #include #include

    using namespace std;

    struct Edge { int u, v, cost; bool operator<(const Edge& e) const { return cost < e.cost; } };

    int find(int x, vector& parent) { if (parent[x] != x) { parent[x] = find(parent[x], parent); } return parent[x]; }

    void unionSets(int x, int y, vector& parent, vector& rank) { int rootX = find(x, parent); int rootY = find(y, parent); if (rootX != rootY) { if (rank[rootX] > rank[rootY]) { parent[rootY] = rootX; } else if (rank[rootX] < rank[rootY]) { parent[rootX] = rootY; } else { parent[rootY] = rootX; rank[rootX]++; } } }

    int main() { int N, M; cin >> N >> M; vector edges(M); for (int i = 0; i < M; ++i) { cin >> edges[i].u >> edges[i].v >> edges[i].cost; } sort(edges.begin(), edges.end());

    vector parent(N + 1);
    vector rank(N + 1, 0);
    for (int i = 1; i <= N; ++i) {
        parent[i] = i;
    }
    
    int mstCost = 0;
    int edgesUsed = 0;
    for (const auto& edge : edges) {
        if (find(edge.u, parent) != find(edge.v, parent)) {
            unionSets(edge.u, edge.v, parent, rank);
            mstCost += edge.cost;
            edgesUsed++;
            if (edgesUsed == N - 1) {
                break;
            }
        }
    }
    
    cout << mstCost << endl;
    return 0;

    }

    代码示例(Prim算法)

    #include #include #include #include

    using namespace std;

    struct Edge { int to, cost; bool operator>(const Edge& e) const { return cost > e.cost; } };

    int main() { int N, M; cin >> N >> M; vector> graph(N + 1); for (int i = 0; i < M; ++i) { int u, v, c; cin >> u >> v >> c; graph[u].push_back({v, c}); graph[v].push_back({u, c}); }

    vector minEdge(N + 1, INT_MAX);
    vector inMST(N + 1, false);
    priority_queue, greater> pq;
    
    // Start from node 1
    minEdge[1] = 0;
    pq.push({1, 0});
    
    int mstCost = 0;
    while (!pq.empty()) {
        Edge current = pq.top();
        pq.pop();
        int u = current.to;
        if (inMST[u]) continue;
        inMST[u] = true;
        mstCost += current.cost;
    
        for (const auto& edge : graph[u]) {
            int v = edge.to;
            int cost = edge.cost;
            if (!inMST[v] && cost < minEdge[v]) {
                minEdge[v] = cost;
                pq.push({v, cost});
            }
        }
    }
    
    cout << mstCost << endl;
    return 0;

    }

    注意事项

    1. 输入输出格式:确保按照题目要求的格式进行输入输出。
    2. 大数据处理:对于大规模数据,注意优化算法的时间和空间复杂度。
    3. 边界条件:处理特殊情况,如所有城市已经连通或没有任何边的情况。

    通过以上代码和思路,可以有效地解决《Highways》这一题目。选择合适的算法和数据结构是关键。

  • Out of Hay,POJ,2395

    《Out of Hay》是POJ(北京大学在线评测系统)上的一个经典问题,题目编号为2395。这个问题属于图论中的最小生成树(Minimum Spanning Tree, MST)问题。下面我会详细解释题目的背景、解题思路以及具体的实现方法。

    题目背景

    题目描述了一个农场主需要用干草喂养分布在各个地点的牛。农场主有一个仓库,里面存储了一定量的干草,而牛分布在不同的地点。每个地点之间的距离是已知的,农场主需要用最少的干草量来喂养所有的牛。问题可以抽象为在一个带权无向图中找到最小生成树,使得总权值最小。

    输入输出

    • 输入
      • 第一行包含两个整数N(地点数,包括仓库)和M(道路数)。
      • 接下来的M行,每行包含三个整数X, Y, Z,表示地点X和地点Y之间的距离为Z。
    • 输出
      • 输出一个整数,表示最小的干草量。

    解题思路

    这个问题可以通过最小生成树算法来解决。常用的最小生成树算法有Kruskal算法和Prim算法。这里以Kruskal算法为例进行讲解。

    Kruskal算法步骤:

    1. 初始化:将所有的边按权值从小到大排序。
    2. 构建森林:初始时每个顶点都是一个独立的集合。
    3. 合并集合
      • 从权值最小的边开始,依次考虑每条边。
      • 如果这条边连接的两个顶点属于不同的集合,则将它们合并成一个集合,并将这条边的权值加入总权值。
      • 如果这条边连接的两个顶点属于同一个集合,则跳过这条边。
    4. 终止条件:当所有的顶点都合并成一个集合时,算法结束。

    代码实现

    以下是使用Kruskal算法实现的C++代码示例:

    #include #include #include

    using namespace std;

    struct Edge { int u, v, w; bool operator<(const Edge& e) const { return w < e.w; } };

    int find(int x, vector& parent) { if (parent[x] != x) { parent[x] = find(parent[x], parent); } return parent[x]; }

    void unionSets(int x, int y, vector& parent, vector& rank) { int rootX = find(x, parent); int rootY = find(y, parent); if (rootX != rootY) { if (rank[rootX] > rank[rootY]) { parent[rootY] = rootX; } else if (rank[rootX] < rank[rootY]) { parent[rootX] = rootY; } else { parent[rootY] = rootX; rank[rootX]++; } } }

    int kruskal(int n, vector& edges) { sort(edges.begin(), edges.end()); vector parent(n); vector rank(n, 0); for (int i = 0; i < n; ++i) { parent[i] = i; } int mstWeight = 0; for (const auto& edge : edges) { int rootU = find(edge.u, parent); int rootV = find(edge.v, parent); if (rootU != rootV) { unionSets(rootU, rootV, parent, rank); mstWeight += edge.w; } } return mstWeight; }

    int main() { int n, m; cin >> n >> m; vector edges(m); for (int i = 0; i < m; ++i) { cin >> edges[i].u >> edges[i].v >> edges[i].w; } cout << kruskal(n, edges) << endl; return 0; }

    解释

    1. Edge结构体:用于存储边的起点、终点和权值。
    2. find函数:用于查找某个顶点的根节点。
    3. unionSets函数:用于合并两个集合。
    4. kruskal函数:实现Kruskal算法,返回最小生成树的总权值。
    5. main函数:读取输入数据,调用kruskal函数并输出结果。

    通过上述代码,可以有效地解决《Out of Hay》问题,找到最小的干草量。希望这个详细的解释和代码示例对你有所帮助!如果有任何进一步的问题,欢迎继续提问。

  • Agri-Net,POJ,1258

    题目描述:Agri-Net (POJ 1258)

    农夫约翰的农场由N (2 <= N <= 100) 个田地组成,这些田地通过M (1 <= M <= 10,000) 条双向的小路连接。每条小路有一个正整数的长度 (1 <= length <= 1,000,000)。农夫约翰希望所有田地都能通过小路直接或间接相连,以便于管理和运输。现在他希望找到一种方案,使得所有田地连通,并且总的小路长度最短。

    输入格式:

    第一行包含一个整数N,表示田地的数量。

    接下来N行,每行包含N个整数,其中第i行的第j个整数表示田地i和田地j之间小路的长度。如果田地i和田地j之间没有直接的小路连接,则该整数为0。

    输出格式:

    输出一个整数,表示所有田地连通所需的最短小路总长度。

    解题思路:

    这是一个经典的最小生成树问题,可以使用Prim算法Kruskal算法来解决。

    Prim算法

    1. 初始化
      • 选择任意一个田地作为起点,将其加入生成树集合。
      • 初始化距离数组,记录每个田地到生成树集合的最短距离。
    2. 迭代
      • 在未加入生成树的田地中,选择距离生成树集合最近的田地,将其加入生成树集合。
      • 更新其他未加入生成树的田地到生成树集合的最短距离。
    3. 终止
      • 当所有田地都加入生成树集合时,算法结束。

    Kruskal算法

    1. 初始化
      • 将所有小路按长度从小到大排序。
    2. 迭代
      • 从小路列表中依次选择一条小路,检查其连接的两个田地是否已经在同一连通分量中。
      • 如果不在同一连通分量中,则将这条小路加入生成树,并将两个田地所在的连通分量合并。
    3. 终止
      • 当生成树包含N-1条边时,算法结束。

    代码实现(Prim算法)

    #include #include #include using namespace std;

    const int MAX_N = 100; int N; int graph[MAX_N][MAX_N]; bool inMST[MAX_N]; int minEdge[MAX_N];

    int prim() { fill(minEdge, minEdge + N, INT_MAX); fill(inMST, inMST + N, false); minEdge[0] = 0; int totalWeight = 0;

    for (int i = 0; i < N; ++i) {
        int u = -1;
        for (int j = 0; j < N; ++j) {
            if (!inMST[j] && (u == -1 || minEdge[j] < minEdge[u])) {
                u = j;
            }
        }
    
        inMST[u] = true;
        totalWeight += minEdge[u];
    
        for (int v = 0; v < N; ++v) {
            if (graph[u][v] && !inMST[v] && graph[u][v] < minEdge[v]) {
                minEdge[v] = graph[u][v];
            }
        }
    }
    
    return totalWeight;

    }

    int main() { cin >> N; for (int i = 0; i < N; ++i) { for (int j = 0; j < N; ++j) { cin >> graph[i][j]; } }

    cout << prim() << endl;
    return 0;

    }

    代码实现(Kruskal算法)

    #include #include #include using namespace std;

    struct Edge { int u, v, weight; bool operator<(const Edge& other) const { return weight < other.weight; } };

    int parent[MAX_N];

    int find(int x) { if (parent[x] != x) { parent[x] = find(parent[x]); } return parent[x]; }

    void unionSets(int x, int y) { int rootX = find(x); int rootY = find(y); parent[rootX] = rootY; }

    int kruskal(vector& edges) { sort(edges.begin(), edges.end()); for (int i = 0; i < N; ++i) { parent[i] = i; }

    int totalWeight = 0;
    int numEdges = 0;
    
    for (const Edge& edge : edges) {
        if (find(edge.u) != find(edge.v)) {
            unionSets(edge.u, edge.v);
            totalWeight += edge.weight;
            numEdges++;
            if (numEdges == N - 1) break;
        }
    }
    
    return totalWeight;

    }

    int main() { cin >> N; vector edges; for (int i = 0; i < N; ++i) { for (int j = 0; j < N; ++j) { int weight; cin >> weight; if (weight > 0) { edges.push_back({i, j, weight}); } } }

    cout << kruskal(edges) << endl;
    return 0;

    }

    总结

    • Prim算法适用于稠密图,时间复杂度为O(N^2)。
    • Kruskal算法适用于稀疏图,时间复杂度为O(ElogE),其中E为边的数量。

    根据题目中N和M的范围,两种算法都可以在规定时间内完成。选择哪种算法可以根据具体情况进行优化。

    希望这个详细的解答能帮助你理解和解决Agri-Net问题。如果有任何进一步的问题,欢迎继续提问!

  • Frogger,POJ,2253

    《Frogger》是一款经典的街机游戏,最初由Konami在1981年发布。玩家需要控制一只青蛙穿越繁忙的公路和湍急的河流,到达对岸的安全区域。游戏的目标是在限定的时间内,将青蛙安全地引导到目的地,同时避免被车辆撞到或掉入水中。

    POJ(Programming Online Judge)是一个在线编程评测系统,广泛用于编程竞赛和算法练习。它提供了大量的编程题目,用户可以提交代码,系统会自动评测代码的正确性和效率。

    至于“2253”,如果你是指POJ上的题目编号2253,那么它对应的是一道具体的编程题目。通常,每个题目都会有一个详细的描述,包括问题的背景、输入输出格式、以及需要满足的条件等。

    例如,POJ 2253的题目可能是关于图论、动态规划或其他算法问题。为了更好地解答这个问题,我们可以具体查看POJ 2253的题目描述。

    假设POJ 2253的题目描述如下:

    题目名称:Frogger

    题目描述: 在一个二维平面上,有一只青蛙需要从起点跳到终点。平面上有一些障碍物,青蛙不能跳到障碍物上。青蛙每次可以向上、下、左、右四个方向跳跃,每次跳跃的距离是固定的。求青蛙从起点跳到终点的最短路径。

    输入格式: 第一行两个整数N和M,表示平面的行数和列数。 接下来N行,每行M个字符,表示平面上的情况。’S’表示起点,’T’表示终点,’#’表示障碍物,’.’表示空地。

    输出格式: 输出一个整数,表示青蛙从起点跳到终点的最短路径长度。如果无法到达终点,输出-1。

    样例输入:

    5 5 ..... .S#.. ...#. ..#.. ....T

    样例输出:

    6

    针对这个题目,我们可以使用广度优先搜索(BFS)算法来求解。BFS适用于寻找最短路径的问题,特别是当每次移动的代价相同的情况下。

    以下是一个可能的Python代码实现:

    from collections import deque

    def bfs(start, end, grid, n, m): directions = [(0, 1), (1, 0), (0, -1), (-1, 0)] queue = deque([(start, 0)]) # (position, distance) visited = set() visited.add(start)

    while queue:
        (x, y), dist = queue.popleft()
        if (x, y) == end:
            return dist
    
        for dx, dy in directions:
            nx, ny = x + dx, y + dy
            if 0 <= nx < n and 0 <= ny < m and grid[nx][ny] != '#' and (nx, ny) not in visited:
                visited.add((nx, ny))
                queue.append(((nx, ny), dist + 1))
    
    return -1

    def main(): n, m = map(int, input().split()) grid = [] start = end = None

    for i in range(n):
        row = input().strip()
        grid.append(row)
        if 'S' in row:
            start = (i, row.index('S'))
        if 'T' in row:
            end = (i, row.index('T'))
    
    result = bfs(start, end, grid, n, m)
    print(result)

    if name == "main": main()

    这个代码首先读取输入,然后使用BFS算法寻找从起点到终点的最短路径。如果找到终点,输出路径长度;否则输出-1。

    希望这个详细的解答对你有所帮助!如果有更多具体的问题或需要进一步的解释,请随时提问。