算法入门篇 之 图的应用

目录

一、最短路径

Dijkstra、Floyd、Bellman ford、SPFA算法

Dijkstra算法

Floyd算法

Bellman-Ford算法

SPFA算法

POJ1797、POJ1860、POJ3259、POJ3268

POJ1797 - Heavy Transportation

POJ1860 - Currency Exchange

POJ3259 - Wormholes

POJ3268 - Silver Cow Party

二、最小生成树

Prim、Kruskal算法

Prim算法

Kruskal算法

Prim和Kruskal算法的对比

POJ1251、POJ1287、POJ2031、POJ2421

POJ1251 - Jungle Roads

POJ1287 - Networking

POJ2031 - Sightseeing tour

POJ2421 - Constructing Roads

三、拓扑排序

POJ2367、POJ1094、POJ3687、POJ1270

POJ2367 - Network

POJ1094 - Sorting It All Out

POJ3687 - Placing Lampposts

POJ1270 - Factorial Remainder

四、关键路径

SDUTOJ2498、HDU4019、POJ1949、HDU1224、HDU1317

SDUTOJ2498 - Prim's MST Algorithm

HDU4019 - Color the Blocks

POJ1949 - Convex Hull Trick

HDU1224 - 3n + 1 Problem

HDU1317 - Game of Connections


一、最短路径

Dijkstra、Floyd、Bellman ford、SPFA算法

Dijkstra算法

特点:

  • 适用于没有负权边的图。
  • 时间复杂度:使用优先队列时为 O((V+E)log⁡V)O((V + E) \log V)O((V+E)logV),其中 V 是顶点数,E 是边数。

适用场景:

  • 单源最短路径。
  • 常用于路由算法和导航系统。

步骤:

  1. 初始化源点的距离为0,其他点的距离为无穷大。
  2. 使用优先队列选择当前距离最小的点。
  3. 更新该点的邻居的距离。
  4. 重复步骤2和3直到所有点都被处理。

Floyd算法

特点:

  • 适用于任意带权图,包括负权边。
  • 时间复杂度: O(V3)O(V^3)O(V3)。

适用场景:

  • 求解所有顶点对的最短路径。
  • 常用于网络分析、交通网络等。

步骤:

  1. 初始化距离矩阵,若有直接边则为边的权重,否则为无穷大,自己到自己为0。
  2. 对每对顶点 (i, j),考虑所有中间点 k,更新 i 到 j 的最短距离。

Bellman-Ford算法

特点:

  • 适用于有负权边的图。
  • 时间复杂度: O(VE)O(VE)O(VE)。

适用场景:

  • 单源最短路径。
  • 可以检测负权环。

步骤:

  1. 初始化源点的距离为0,其他点的距离为无穷大。
  2. 对所有边进行 V-1 次松弛操作。
  3. 检测负权环,如果在第 V 次松弛操作中还有距离更新,则存在负权环。

SPFA算法

特点:

  • Bellman-Ford算法的优化版本。
  • 平均情况下比 Bellman-Ford 更快,时间复杂度为 O(kE)O(kE)O(kE),k 通常为 2~3。

适用场景:

  • 单源最短路径,尤其在稀疏图和有负权边时。

步骤:

  1. 初始化源点的距离为0,其他点的距离为无穷大,并将源点入队。
  2. 处理队列中的点,松弛其所有邻居的距离,并将更新后的邻居入队。
  3. 重复直到队列为空。

POJ1797、POJ1860、POJ3259、POJ3268

POJ1797 - Heavy Transportation

描述:

  • 给定一个带权无向图,找到从源点到目标点的最大最小边权,即从源到目标路径中最小的边权最大。

推荐算法:

  • Dijkstra's Algorithm 的变种(最大最小路径)。
    • 使用最大堆(优先队列)来存储边权。
    • 每次选择最大权边,并更新路径上的最小边权。

Python代码示例

import heapq

def dijkstra_max_min(graph, start, end):
    max_heap = [(-float('inf'), start)]  # (最大边权,节点)
    min_edge = {start: float('inf')}
    
    while max_heap:
        min_val, u = heapq.heappop(max_heap)
        min_val = -min_val
        
        if u == end:
            return min_val
        
        for v, weight in graph[u]:
            next_val = min(min_val, weight)
            if v not in min_edge or next_val > min_edge[v]:
                min_edge[v] = next_val
                heapq.heappush(max_heap, (-next_val, v))
                
    return -1

# 读取输入并构建图

POJ1860 - Currency Exchange

描述:

  • 货币兑换问题,判断是否存在一种方式可以通过一系列的货币兑换使得初始货币增值。

推荐算法:

  • Bellman-Ford Algorithm(检测负环)。
    • 将每种兑换率转换为图中的边权(使用对数换算:log(rate)),从而将乘法转换为加法。
    • 检测是否存在负环,负环表示可以通过兑换获得更多的初始货币。

Python代码示例

import math

def bellman_ford_currency_exchange(graph, start, n):
    dist = [-float('inf')] * n
    dist[start] = 0
    
    for _ in range(n - 1):
        for u in range(n):
            for v, rate in graph[u]:
                if dist[u] + math.log(rate) > dist[v]:
                    dist[v] = dist[u] + math.log(rate)
                    
    for u in range(n):
        for v, rate in graph[u]:
            if dist[u] + math.log(rate) > dist[v]:
                return True
    
    return False

# 读取输入并构建图

POJ3259 - Wormholes

描述:

  • 给定一个图,其中包括一些负权边,判断是否存在负权环。

推荐算法:

  • Bellman-Ford Algorithm(检测负环)。
    • 执行 V-1 次松弛操作后,再进行一次松弛操作,如果仍有边可以松弛,则存在负权环。

Python代码示例

def bellman_ford_negative_cycle(graph, n):
    dist = [float('inf')] * n
    dist[0] = 0
    
    for _ in range(n - 1):
        for u in range(n):
            for v, weight in graph[u]:
                if dist[u] + weight < dist[v]:
                    dist[v] = dist[u] + weight
                    
    for u in range(n):
        for v, weight in graph[u]:
            if dist[u] + weight < dist[v]:
                return True
    
    return False

# 读取输入并构建图

POJ3268 - Silver Cow Party

描述:

  • 一个农场的奶牛要去参加聚会,要求计算每头奶牛从家到聚会地点再返回家的最短路径。

推荐算法:

  • Dijkstra's Algorithm(单源最短路径)。
    • 分别计算每个奶牛从家到聚会地点的最短路径和从聚会地点回家的最短路径。
    • 将两者相加得到每头奶牛的总路径长度。

Python代码示例

import heapq

def dijkstra(graph, start, n):
    dist = [float('inf')] * n
    dist[start] = 0
    priority_queue = [(0, start)]
    
    while priority_queue:
        current_dist, u = heapq.heappop(priority_queue)
        
        if current_dist > dist[u]:
            continue
        
        for v, weight in graph[u]:
            distance = current_dist + weight
            if distance < dist[v]:
                dist[v] = distance
                heapq.heappush(priority_queue, (distance, v))
    
    return dist

# 读取输入并构建图

二、最小生成树

Prim、Kruskal算法

Prim和Kruskal算法都是经典的最小生成树(MST)算法,用于在一个无向加权图中找到一棵包含所有顶点且总权重最小的树。

Prim算法

特点:

  • 从一个起始顶点开始构建最小生成树。
  • 使用优先队列(最小堆)来选择当前最小权重边。
  • 时间复杂度: O((V+E)log⁡V)O((V + E) \log V)O((V+E)logV),适用于稠密图。

步骤:

  1. 初始化:选择一个起点,并将该点加入MST集合。
  2. 使用最小堆将与MST集合相连的边按权重排序。
  3. 选择权重最小的边,并将该边连接的另一个顶点加入MST集合。
  4. 更新最小堆,包含新的顶点所连接的边。
  5. 重复步骤2-4,直到所有顶点都被包含在MST集合中。

示例代码:

import heapq

def prim(graph, start):
    mst = []
    visited = set([start])
    edges = [(weight, start, to) for to, weight in graph[start]]
    heapq.heapify(edges)

    while edges:
        weight, frm, to = heapq.heappop(edges)
        if to not in visited:
            visited.add(to)
            mst.append((frm, to, weight))
            
            for next_to, next_weight in graph[to]:
                if next_to not in visited:
                    heapq.heappush(edges, (next_weight, to, next_to))

    return mst

# 示例图的表示方法
# graph = {0: [(1, 4), (2, 1)], 1: [(0, 4), (2, 3), (3, 2)], 2: [(0, 1), (1, 3), (3, 4)], 3: [(1, 2), (2, 4)]}

Kruskal算法

特点:

  • 从边的角度出发,按权重从小到大排序,然后逐一选择边。
  • 使用并查集(Union-Find)来检测是否形成环。
  • 时间复杂度: O(Elog⁡E+Eα(V))O(E \log E + E \alpha(V))O(ElogE+Eα(V)),适用于稀疏图。

步骤:

  1. 初始化:将所有边按权重从小到大排序。
  2. 使用并查集初始化每个顶点所属的集合。
  3. 依次选择权重最小的边,如果边的两个顶点属于不同集合,则将该边加入MST,并合并两个集合。
  4. 重复步骤3,直到MST包含 V−1V-1V−1 条边。

示例代码:

class UnionFind:
    def __init__(self, n):
        self.parent = list(range(n))
        self.rank = [0] * n

    def find(self, u):
        if self.parent[u] != u:
            self.parent[u] = self.find(self.parent[u])
        return self.parent[u]

    def union(self, u, v):
        root_u = self.find(u)
        root_v = self.find(v)
        if root_u != root_v:
            if self.rank[root_u] > self.rank[root_v]:
                self.parent[root_v] = root_u
            elif self.rank[root_u] < self.rank[root_v]:
                self.parent[root_u] = root_v
            else:
                self.parent[root_v] = root_u
                self.rank[root_u] += 1

def kruskal(graph, n):
    edges = [(weight, u, v) for u in range(n) for v, weight in graph[u]]
    edges.sort()
    uf = UnionFind(n)
    mst = []

    for weight, u, v in edges:
        if uf.find(u) != uf.find(v):
            uf.union(u, v)
            mst.append((u, v, weight))

    return mst

# 示例图的表示方法
# graph = {0: [(1, 4), (2, 1)], 1: [(0, 4), (2, 3), (3, 2)], 2: [(0, 1), (1, 3), (3, 4)], 3: [(1, 2), (2, 4)]}

Prim和Kruskal算法的对比

  1. Prim算法:

    • 更适用于稠密图,因为其时间复杂度与边的数量关系密切。
    • 需要访问已在MST中的顶点及其邻接边。
    • 边的选择是局部的,逐步扩展MST。
  2. Kruskal算法:

    • 更适用于稀疏图,因为其时间复杂度主要取决于边的数量。
    • 需要对所有边进行全局排序。
    • 边的选择是全局的,每次选择权重最小且不形成环的边。

POJ1251、POJ1287、POJ2031、POJ2421

POJ1251 - Jungle Roads

描述:

  • 给定一个带权无向图,表示一个村庄的路径,要求找到覆盖所有村庄的最小路径总和,即最小生成树(MST)。

推荐算法:

  • Kruskal's AlgorithmPrim's Algorithm

示例代码:

使用 Kruskal's Algorithm:

class UnionFind:
    def __init__(self, n):
        self.parent = list(range(n))
        self.rank = [0] * n

    def find(self, u):
        if self.parent[u] != u:
            self.parent[u] = self.find(self.parent[u])
        return self.parent[u]

    def union(self, u, v):
        root_u = self.find(u)
        root_v = self.find(v)
        if root_u != root_v:
            if self.rank[root_u] > self.rank[root_v]:
                self.parent[root_v] = root_u
            elif self.rank[root_u] < self.rank[root_v]:
                self.parent[root_u] = root_v
            else:
                self.parent[root_v] = root_u
                self.rank[root_u] += 1

def kruskal(n, edges):
    edges.sort(key=lambda x: x[2])
    uf = UnionFind(n)
    mst_weight = 0
    mst_edges = []

    for u, v, weight in edges:
        if uf.find(u) != uf.find(v):
            uf.union(u, v)
            mst_weight += weight
            mst_edges.append((u, v, weight))

    return mst_weight

# 读取输入并构建图
# edges = [(u, v, weight), ...]
# n = number of nodes
# result = kruskal(n, edges)

POJ1287 - Networking

描述:

  • 给定一个带权无向图,表示网络连接,要求找到最小生成树(MST)。

推荐算法:

  • Kruskal's AlgorithmPrim's Algorithm

示例代码:

使用 Prim's Algorithm:

import heapq

def prim(graph, start):
    mst_weight = 0
    visited = set([start])
    edges = [(weight, start, to) for to, weight in graph[start]]
    heapq.heapify(edges)

    while edges:
        weight, frm, to = heapq.heappop(edges)
        if to not in visited:
            visited.add(to)
            mst_weight += weight
            for next_to, next_weight in graph[to]:
                if next_to not in visited:
                    heapq.heappush(edges, (next_weight, to, next_to))

    return mst_weight

# 读取输入并构建图
# graph = {0: [(1, 4), (2, 1)], 1: [(0, 4), (2, 3), (3, 2)], 2: [(0, 1), (1, 3), (3, 4)], 3: [(1, 2), (2, 4)]}
# result = prim(graph, start_node)

POJ2031 - Sightseeing tour

描述:

  • 给定一个带权无向图,表示景点之间的路径,要求找到一条从某个景点出发,经过所有景点再回到原点的最小路径,即旅行商问题(TSP)。

推荐算法:

  • 动态规划近似算法(如遗传算法、模拟退火等)。

示例代码:

使用 动态规划(Held-Karp算法):

def tsp(graph):
    n = len(graph)
    dp = [[float('inf')] * n for _ in range(1 << n)]
    dp[1][0] = 0

    for mask in range(1 << n):
        for u in range(n):
            if mask & (1 << u):
                for v in range(n):
                    if not mask & (1 << v):
                        dp[mask | (1 << v)][v] = min(dp[mask | (1 << v)][v], dp[mask][u] + graph[u][v])

    return min(dp[(1 << n) - 1][v] + graph[v][0] for v in range(1, n))

# 读取输入并构建图
# graph = [[0, 29, 20, 21], [29, 0, 15, 17], [20, 15, 0, 28], [21, 17, 28, 0]]
# result = tsp(graph)

POJ2421 - Constructing Roads

描述:

  • 给定一个带权无向图,要求找到最小生成树(MST)。

推荐算法:

  • Kruskal's AlgorithmPrim's Algorithm

示例代码:

使用 Kruskal's Algorithm:

class UnionFind:
    def __init__(self, n):
        self.parent = list(range(n))
        self.rank = [0] * n

    def find(self, u):
        if self.parent[u] != u:
            self.parent[u] = self.find(self.parent[u])
        return self.parent[u]

    def union(self, u, v):
        root_u = self.find(u)
        root_v = self.find(v)
        if root_u != root_v:
            if self.rank[root_u] > self.rank[root_v]:
                self.parent[root_v] = root_u
            elif self.rank[root_u] < self.rank[root_v]:
                self.parent[root_u] = root_v
            else:
                self.parent[root_v] = root_u
                self.rank[root_u] += 1

def kruskal(n, edges):
    edges.sort(key=lambda x: x[2])
    uf = UnionFind(n)
    mst_weight = 0
    mst_edges = []

    for u, v, weight in edges:
        if uf.find(u) != uf.find(v):
            uf.union(u, v)
            mst_weight += weight
            mst_edges.append((u, v, weight))

    return mst_weight

# 读取输入并构建图
# edges = [(u, v, weight), ...]
# n = number of nodes
# result = kruskal(n, edges)


三、拓扑排序

POJ2367、POJ1094、POJ3687、POJ1270

POJ2367 - Network

描述:

  • 给定一个有向图,要求找到图中所有的强连通分量(SCC)。

推荐算法:

  • Tarjan's AlgorithmKosaraju's Algorithm

示例代码:

使用 Tarjan's Algorithm:

def tarjan_scc(graph):
    n = len(graph)
    index = 0
    stack = []
    indices = [-1] * n
    lowlinks = [-1] * n
    on_stack = [False] * n
    sccs = []

    def strongconnect(v):
        nonlocal index
        indices[v] = index
        lowlinks[v] = index
        index += 1
        stack.append(v)
        on_stack[v] = True

        for w in graph[v]:
            if indices[w] == -1:
                strongconnect(w)
                lowlinks[v] = min(lowlinks[v], lowlinks[w])
            elif on_stack[w]:
                lowlinks[v] = min(lowlinks[v], indices[w])

        if lowlinks[v] == indices[v]:
            scc = []
            while True:
                w = stack.pop()
                on_stack[w] = False
                scc.append(w)
                if w == v:
                    break
            sccs.append(scc)

    for v in range(n):
        if indices[v] == -1:
            strongconnect(v)

    return sccs

# 示例图的表示方法
# graph = {0: [1], 1: [2], 2: [0, 3], 3: [4], 4: [5], 5: [3]}
# result = tarjan_scc(graph)

POJ1094 - Sorting It All Out

描述:

  • 给定一组不等式关系,要求判断是否存在唯一的拓扑排序,或者检测是否存在矛盾。

推荐算法:

  • 拓扑排序 结合 Kahn's Algorithm

示例代码:

from collections import deque, defaultdict

def topological_sort(n, edges):
    in_degree = [0] * n
    graph = defaultdict(list)

    for u, v in edges:
        graph[u].append(v)
        in_degree[v] += 1

    queue = deque([i for i in range(n) if in_degree[i] == 0])
    topo_order = []
    unique = True

    while queue:
        if len(queue) > 1:
            unique = False
        node = queue.popleft()
        topo_order.append(node)
        for neighbor in graph[node]:
            in_degree[neighbor] -= 1
            if in_degree[neighbor] == 0:
                queue.append(neighbor)

    if len(topo_order) != n:
        return "Inconsistency found."
    elif not unique:
        return "Sorted sequence cannot be determined."
    else:
        return " ".join(map(str, topo_order))

# 示例输入
# n = 4
# edges = [(0, 1), (1, 2), (3, 1)]
# result = topological_sort(n, edges)

POJ3687 - Placing Lampposts

描述:

  • 给定一个网格,每个点要么是空地要么是建筑,要求放置尽量少的路灯照亮所有空地。

推荐算法:

  • 二分图最大匹配,即 Hopcroft-Karp Algorithm

示例代码:

class BipartiteMatcher:
    def __init__(self, n, m):
        self.n = n
        self.m = m
        self.edges = [[] for _ in range(n)]
        self.left_match = [-1] * n
        self.right_match = [-1] * m
        self.dist = [-1] * n

    def add_edge(self, u, v):
        self.edges[u].append(v)

    def bfs(self):
        queue = []
        for i in range(self.n):
            if self.left_match[i] == -1:
                self.dist[i] = 0
                queue.append(i)
            else:
                self.dist[i] = float('inf')
        found_augmenting_path = False
        for u in queue:
            if self.dist[u] >= 0:
                for v in self.edges[u]:
                    if self.right_match[v] == -1:
                        found_augmenting_path = True
                    elif self.dist[self.right_match[v]] == float('inf'):
                        self.dist[self.right_match[v]] = self.dist[u] + 1
                        queue.append(self.right_match[v])
        return found_augmenting_path

    def dfs(self, u):
        if u != -1:
            for v in self.edges[u]:
                if self.right_match[v] == -1 or (self.dist[self.right_match[v]] == self.dist[u] + 1 and self.dfs(self.right_match[v])):
                    self.left_match[u] = v
                    self.right_match[v] = u
                    return True
            self.dist[u] = float('inf')
            return False
        return True

    def maximum_matching(self):
        matching = 0
        while self.bfs():
            for i in range(self.n):
                if self.left_match[i] == -1:
                    if self.dfs(i):
                        matching += 1
        return matching

# 示例使用
# n, m = number of vertices on each side of bipartite graph
# matcher = BipartiteMatcher(n, m)
# matcher.add_edge(u, v) # 添加二分图中的边
# result = matcher.maximum_matching()

POJ1270 - Factorial Remainder

描述:

  • 给定一个大整数N和一个小整数M,求N!对M取模的结果。

推荐算法:

  • 数论,利用模运算性质和中国剩余定理(CRT)。

示例代码:

def factorial_mod(n, m):
    if n >= m:
        return 0
    result = 1
    for i in range(2, n + 1):
        result = (result * i) % m
    return result

# 示例输入
# n = 10
# m = 7
# result = factorial_mod(n, m)


四、关键路径

SDUTOJ2498、HDU4019、POJ1949、HDU1224、HDU1317

SDUTOJ2498 - Prim's MST Algorithm

描述:

  • 该题需要你实现Prim算法来找到最小生成树(MST)。

推荐算法:

  • Prim's Algorithm

示例代码:

import heapq

def prim(graph, start):
    mst_weight = 0
    visited = set([start])
    edges = [(weight, start, to) for to, weight in graph[start]]
    heapq.heapify(edges)

    while edges:
        weight, frm, to = heapq.heappop(edges)
        if to not in visited:
            visited.add(to)
            mst_weight += weight
            for next_to, next_weight in graph[to]:
                if next_to not in visited:
                    heapq.heappush(edges, (next_weight, to, next_to))

    return mst_weight

# 示例输入
# graph = {0: [(1, 4), (2, 1)], 1: [(0, 4), (2, 3), (3, 2)], 2: [(0, 1), (1, 3), (3, 4)], 3: [(1, 2), (2, 4)]}
# start_node = 0
# result = prim(graph, start_node)

HDU4019 - Color the Blocks

描述:

  • 给定一个由黑白方块组成的矩阵,要求找到最小覆盖顶点集。

推荐算法:

  • 二分图最大匹配,即 Hopcroft-Karp Algorithm

示例代码:

class BipartiteMatcher:
    def __init__(self, n, m):
        self.n = n
        self.m = m
        self.edges = [[] for _ in range(n)]
        self.left_match = [-1] * n
        self.right_match = [-1] * m
        self.dist = [-1] * n

    def add_edge(self, u, v):
        self.edges[u].append(v)

    def bfs(self):
        queue = []
        for i in range(self.n):
            if self.left_match[i] == -1:
                self.dist[i] = 0
                queue.append(i)
            else:
                self.dist[i] = float('inf')
        found_augmenting_path = False
        for u in queue:
            if self.dist[u] >= 0:
                for v in self.edges[u]:
                    if self.right_match[v] == -1:
                        found_augmenting_path = True
                    elif self.dist[self.right_match[v]] == float('inf'):
                        self.dist[self.right_match[v]] = self.dist[u] + 1
                        queue.append(self.right_match[v])
        return found_augmenting_path

    def dfs(self, u):
        if u != -1:
            for v in self.edges[u]:
                if self.right_match[v] == -1 or (self.dist[self.right_match[v]] == self.dist[u] + 1 and self.dfs(self.right_match[v])):
                    self.left_match[u] = v
                    self.right_match[v] = u
                    return True
            self.dist[u] = float('inf')
            return False
        return True

    def maximum_matching(self):
        matching = 0
        while self.bfs():
            for i in range(self.n):
                if self.left_match[i] == -1:
                    if self.dfs(i):
                        matching += 1
        return matching

# 示例使用
# n, m = number of vertices on each side of bipartite graph
# matcher = BipartiteMatcher(n, m)
# matcher.add_edge(u, v) # 添加二分图中的边
# result = matcher.maximum_matching()

POJ1949 - Convex Hull Trick

描述:

  • 给定一组线段,要求最大化y值。

推荐算法:

  • 动态规划结合凸包优化(Convex Hull Trick)。

示例代码:

class ConvexHullTrick:
    def __init__(self):
        self.hull = []

    def add_line(self, a, b):
        while len(self.hull) >= 2 and self._is_bad(self.hull[-2], self.hull[-1], (a, b)):
            self.hull.pop()
        self.hull.append((a, b))

    def _is_bad(self, line1, line2, line3):
        return (line3[1] - line1[1]) * (line1[0] - line2[0]) < (line2[1] - line1[1]) * (line1[0] - line3[0])

    def query(self, x):
        while len(self.hull) >= 2 and self._eval(self.hull[0], x) <= self._eval(self.hull[1], x):
            self.hull.pop(0)
        return self._eval(self.hull[0], x)

    def _eval(self, line, x):
        return line[0] * x + line[1]

# 示例使用
# cht = ConvexHullTrick()
# cht.add_line(a, b)
# result = cht.query(x)

HDU1224 - 3n + 1 Problem

描述:

  • 给定一个整数范围,要求在该范围内找到3n+1序列长度的最大值。

推荐算法:

  • 直接模拟计算

示例代码:

def collatz_length(n):
    length = 1
    while n != 1:
        if n % 2 == 0:
            n //= 2
        else:
            n = 3 * n + 1
        length += 1
    return length

def max_collatz_in_range(start, end):
    max_length = 0
    for i in range(start, end + 1):
        max_length = max(max_length, collatz_length(i))
    return max_length

# 示例输入
# start, end = 1, 10
# result = max_collatz_in_range(start, end)

HDU1317 - Game of Connections

描述:

  • 给定一个网格,每个点要么是空地要么是建筑,要求放置尽量少的路灯照亮所有空地。

推荐算法:

  • 二分图最大匹配,即 Hopcroft-Karp Algorithm

示例代码:

class BipartiteMatcher:
    def __init__(self, n, m):
        self.n = n
        self.m = m
        self.edges = [[] for _ in range(n)]
        self.left_match = [-1] * n
        self.right_match = [-1] * m
        self.dist = [-1] * n

    def add_edge(self, u, v):
        self.edges[u].append(v)

    def bfs(self):
        queue = []
        for i in range(self.n):
            if self.left_match[i] == -1:
                self.dist[i] = 0
                queue.append(i)
            else:
                self.dist[i] = float('inf')
        found_augmenting_path = False
        for u in queue:
            if self.dist[u] >= 0:
                for v in self.edges[u]:
                    if self.right_match[v] == -1:
                        found_augmenting_path = True
                    elif self.dist[self.right_match[v]] == float('inf'):
                        self.dist[self.right_match[v]] = self.dist[u] + 1
                        queue.append(self.right_match[v])
        return found_augmenting_path

    def dfs(self, u):
        if u != -1:
            for v in self.edges[u]:
                if self.right_match[v] == -1 or (self.dist[self.right_match[v]] == self.dist[u] + 1 and self.dfs(self.right_match[v])):
                    self.left_match[u] = v
                    self.right_match[v] = u
                    return True
            self.dist[u] = float('inf')
            return False
        return True

    def maximum_matching(self):
        matching = 0
        while self.bfs():
            for i in range(self.n):
                if self.left_match[i] == -1:
                    if self.dfs(i):
                        matching += 1
        return matching

# 示例使用
# n, m = number of vertices on each side of bipartite graph
# matcher = BipartiteMatcher(n, m)
# matcher.add_edge(u, v) # 添加二分图中的边
# result = matcher.maximum_matching()

  • 20
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

战族狼魂

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值