算法进阶篇 之 实用数据结构
目录
一、并查集
1. HDU 1232: Game (Hangman)
2. POJ 1988: Catch the 'M'
3. POJ 1182: A Special Cake
4. POJ 1703: Calculation
二、优先队列
1. HDU 4006: Fractional Knapsack Problem
2. POJ 3253: Curves
3. POJ 2833: Hopscotch
4. POJ 2431: How Many Trees
一、并查集
1. HDU 1232: Game (Hangman)
问题概述: 玩家在有限的猜测次数内尝试猜出一个隐藏单词。每次猜测会给出当前已猜出的单词状态,并且玩家可以最多进行一定次数的错误猜测。
解题思路:
- 输入处理:读取隐藏单词和猜测序列。
- 状态初始化:
- 使用字符数组来跟踪当前已猜出的字母状态。
- 维护一个失败次数计数器。
- 猜测处理:
- 遍历每个猜测字母,更新单词状态并检查是否达到失败次数上限。
- 结果判断:
- 如果所有字母都被猜中,玩家获胜。
- 如果失败次数超过上限,玩家失败。
Python 示例代码:
def hangman_game(secret_word, guesses, max_attempts):
guessed_letters = set()
incorrect_guesses = 0
word_set = set(secret_word)
current_state = ['_'] * len(secret_word)
for guess in guesses:
if guess in word_set:
guessed_letters.add(guess)
current_state = [ch if ch in guessed_letters else '_' for ch in secret_word]
else:
incorrect_guesses += 1
if set(current_state) == word_set:
return "You win."
if incorrect_guesses >= max_attempts:
return "You lose."
return "You lose." if incorrect_guesses >= max_attempts else "You win."
# Example usage
t = int(input())
for _ in range(t):
max_attempts = int(input())
guesses = [input().strip() for _ in range(max_attempts)]
secret_word = input().strip()
print(hangman_game(secret_word, guesses, max_attempts))
2. POJ 1988: Catch the 'M'
问题概述: 在一个棋盘上放置最大数量的 'M',满足一定的约束条件。
解题思路:
这个问题可以建模为最大流问题或最大匹配问题。棋盘被转换为一个流网络,在网络中寻找最大流。
Python 示例代码:
from collections import defaultdict, deque
def max_bipartite_matching(graph, left_nodes, right_nodes):
match = {}
def bfs():
dist = {u: float('inf') for u in left_nodes}
queue = deque()
for u in left_nodes:
if u not in match:
dist[u] = 0
queue.append(u)
found_augmenting_path = False
while queue:
u = queue.popleft()
for v in graph[u]:
if v not in match:
found_augmenting_path = True
elif dist.get(match[v], float('inf')) == float('inf'):
dist[match[v]] = dist[u] + 1
queue.append(match[v])
return found_augmenting_path
def dfs(u):
for v in graph[u]:
if v not in match or (dist[match[v]] == dist[u] + 1 and dfs(match[v])):
match[v] = u
return True
dist[u] = float('inf')
return False
matching_size = 0
while bfs():
for u in left_nodes:
if u not in match:
if dfs(u):
matching_size += 1
return matching_size
3. POJ 1182: A Special Cake
问题概述: 将一个蛋糕切割成特定形状或大小的块,以满足特定条件。
解题思路:
通常涉及几何计算或特殊的切割规则。你可以将其建模为几何问题或使用动态规划进行解决,具体取决于要求。
Python 示例代码:
def special_cake(cuts):
# Example solution; details depend on the specific problem constraints
# Use geometric calculations or dynamic programming
return len(cuts) # Placeholder for the actual calculation
# Example usage
cuts = [int(x) for x in input().split()]
print(special_cake(cuts))
4. POJ 1703: Calculation
问题概述: 计算给定数学表达式的值。表达式可能包含加、减、乘、除操作符。
解题思路:
- 解析表达式:使用逆波兰表示法(后缀表达式)来评估表达式。
- 实现算法:
- 将表达式转换为逆波兰表示法。
- 使用栈来评估逆波兰表达式。
Python 示例代码:
def evaluate_expression(expression):
def apply_operation(op, a, b):
if op == '+':
return a + b
elif op == '-':
return a - b
elif op == '*':
return a * b
elif op == '/':
return a // b
def precedence(op):
if op in ('+', '-'):
return 1
if op in ('*', '/'):
return 2
return 0
def infix_to_postfix(expression):
output = []
ops = []
for char in expression:
if char.isdigit():
output.append(int(char))
elif char in ('+', '-', '*', '/'):
while (ops and precedence(ops[-1]) >= precedence(char)):
output.append(ops.pop())
ops.append(char)
while ops:
output.append(ops.pop())
return output
def evaluate_postfix(postfix):
stack = []
for token in postfix:
if isinstance(token, int):
stack.append(token)
else:
b = stack.pop()
a = stack.pop()
stack.append(apply_operation(token, a, b))
return stack[0]
postfix_expr = infix_to_postfix(expression)
return evaluate_postfix(postfix_expr)
# Example usage
expression = input().strip()
print(evaluate_expression(expression))
二、优先队列
1. HDU 4006: Fractional Knapsack Problem
问题概述: 这是一个经典的分数背包问题。给定一组物品,每个物品有一个重量和一个价值,背包有一个容量。目标是选择物品,以最大化背包的总价值,允许将物品分割成任意小的部分。
解题思路:
- 计算价值密度:每个物品的价值密度是其价值与重量的比值。
- 排序:根据价值密度从高到低对物品进行排序。
- 选择物品:从价值密度最高的物品开始,尽可能多地装入背包。如果当前物品不能完全放入背包,则将其部分放入。
- 累计价值:更新背包的总价值和剩余容量。
Python 示例代码:
def fractional_knapsack(weights, values, capacity):
items = list(zip(weights, values))
items.sort(key=lambda x: x[1] / x[0], reverse=True)
total_value = 0.0
for weight, value in items:
if capacity == 0:
break
take_weight = min(weight, capacity)
total_value += take_weight * (value / weight)
capacity -= take_weight
return total_value
# Example usage
n, capacity = map(int, input().split())
weights = []
values = []
for _ in range(n):
weight, value = map(int, input().split())
weights.append(weight)
values.append(value)
print(fractional_knapsack(weights, values, capacity))
2. POJ 3253: Curves
问题概述: 给定一些曲线段,每条曲线段由两个点确定。任务是找到最短的路径,使得路径经过每条曲线段恰好一次,类似于最小生成树问题中的最小连接问题。
解题思路:
- 建图:将每条曲线段建成图的边。
- 计算最短路径:使用 Kruskal 算法或 Prim 算法来构建最小生成树。
- 输出结果:最小生成树的边权和即为解决方案。
Python 示例代码:
import heapq
def min_spanning_tree(n, edges):
adj = [[] for _ in range(n)]
for u, v, w in edges:
adj[u].append((w, v))
adj[v].append((w, u))
total_weight = 0
visited = [False] * n
min_heap = [(0, 0)] # Start with node 0
while min_heap:
weight, u = heapq.heappop(min_heap)
if visited[u]:
continue
visited[u] = True
total_weight += weight
for w, v in adj[u]:
if not visited[v]:
heapq.heappush(min_heap, (w, v))
return total_weight
# Example usage
n, m = map(int, input().split())
edges = [tuple(map(int, input().split())) for _ in range(m)]
print(min_spanning_tree(n, edges))
3. POJ 2833: Hopscotch
问题概述: 在一个矩阵中,每个单元格包含一个整数。任务是从矩阵的左上角到右下角找到一条路径,使得路径上的数字之和最大化,只能向下或向右移动。
解题思路:
- 动态规划:定义
dp[i][j]
为从(0, 0)
到(i, j)
的最大和。 - 状态转移:
dp[i][j]
可以从dp[i-1][j]
或dp[i][j-1]
转移过来。 - 初始化:
dp[0][0]
初始化为矩阵的起始值。 - 结果:
dp[n-1][m-1]
是目标结果。
Python 示例代码:
def hopscotch(matrix):
n = len(matrix)
m = len(matrix[0])
dp = [[0] * m for _ in range(n)]
dp[0][0] = matrix[0][0]
for i in range(n):
for j in range(m):
if i > 0:
dp[i][j] = max(dp[i][j], dp[i-1][j] + matrix[i][j])
if j > 0:
dp[i][j] = max(dp[i][j], dp[i][j-1] + matrix[i][j])
return dp[n-1][m-1]
# Example usage
n, m = map(int, input().split())
matrix = [list(map(int, input().split())) for _ in range(n)]
print(hopscotch(matrix))
4. POJ 2431: How Many Trees
问题概述: 给定一个无向图,任务是计算该图中可能的树的数量。树是一个无环的连通图,问题可以通过计算图的不同生成树来解决。
解题思路:
- 计算生成树数量:使用 Kirchoff 的矩阵树定理。
- 构造拉普拉斯矩阵:从图的邻接矩阵构造拉普拉斯矩阵。
- 计算行列式:计算拉普拉斯矩阵的任意子矩阵的行列式作为树的数量。
Python 示例代码:
import numpy as np
def count_trees(n, edges):
laplacian = np.zeros((n, n))
for u, v in edges:
laplacian[u][u] += 1
laplacian[v][v] += 1
laplacian[u][v] -= 1
laplacian[v][u] -= 1
# Remove last row and column
minor = laplacian[:-1, :-1]
# Calculate the determinant
return int(round(np.linalg.det(minor)))
# Example usage
n, m = map(int, input().split())
edges = [tuple(map(int, input().split())) for _ in range(m)]
print(count_trees(n, edges))