算法思想
公式拆分 :
P9231 [蓝桥杯 2023 省 A] 平方差(拆分问题)-CSDN博客
Note: 得到 x=(y−z)×(y+z),可知 x满足可以拆成 2个奇偶性相同的数的乘积。
可图性判断:
Note: 重复移除度序列中的最大值并减少相应数量的其他度值
若最终序列中所有数值都减至零,则说明该序列是可图的,反之若出现负数则不可图。
def havel_hakimi(sequence):
while True:
# 移除序列中所有的0并排序
if not sequence:
# 所有元素处理完毕且不含负数,序列可图
n = sequence.pop(0) # 移除并获取第一个元素
if n > len(sequence):
# 如果n大于序列的长度,无法继续,序列不可图
# 从序列的剩余部分中减去1
for i in range(n):
sequence[i] -= 1
if sequence[i] < 0:
# 如果出现负数,序列不可图
双指针遍历:
局部最优解导向全局最优解: 每次移动较短的板,尝试找到一个更高的板,这样在减少宽度的同时,有机会增加高度,从而找到一个可能更大的容器。因为如果移动较长的板,高度不会增加,而宽度减少
def max_water_container(height):
# 初始化左右指针
# 当左右指针未相遇时
while left < right:
# 计算当前容器能盛放的水量
water = min(height[left], height[right]) * (right - left)
# 更新最大水量
max_water = max(max_water, water)
# 移动指向较短板的指针
if height[left] < height[right]:
left += 1
else:
right -= 1
# 返回计算出的最大水量
return max_water
前缀和:
P8649 [蓝桥杯 2017 省 B] k 倍区间(前缀和+优化(桶分类))_p8649 [蓝桥杯 2017 省 b] k 倍区间 题解-CSDN博客
Note: 首先滑动窗口计算各前缀和取模K,计算同余两前缀和之差(为K的倍数)的个数
main(){
while(temp)
sum = (sum + temp) % k;
count1[sum]++;
// 单一前缀和为k的倍数(特判)
ans += (count1[0] * (count1[0] + 1) / 2)
for (int i = 1; i < k; i++)
// 单一前缀和,不为k的倍数
ans += (count1[i] * (count1[i] - 1) / 2);
}
素数判断:
Note: 利用定理筛选:素数只存在6的倍数附近(即6k+1,6k-1)
再用遍历范围5~sqrt(num),+=6验证
二分查找:
求方程单解:
Note: 二分搜索逼近给定方程的解 x ,直到得到的解与目标值的差值满足特定的精度要求
# 初始化 x, (min, max)为解的范围 eg.(0,100)
x = (min_val + max_val) / 2
sum_val = x * (3 + x * (2 + x * (7 + 8 * x))) + 6
# 二分查找逼近精度
while abs(sum_val - y) > 1e-3:
# 如果 sum 小于 y
if sum_val < y:
min_val = x
x = (max_val + x) / 2
else:
max_val = x
x = (min_val + x) / 2
# 重新计算 sum
sum_val = x * (3 + x * (2 + x * (7 + 8 * x))) + 6
方程多解:
Note: 循环遍历可能的区间,判断区间内是否存在解,二分法逼近解
def f(n):
# 定义多项式函数
return a * n**3 + b * n**2 + c * n + d
def find_roots():
s = 0 # 记录找到的解的个数
for i in range(-100, 100):
min_val = i
max_val = i + 1
# 判断左端点是否为零点,若是解加一
if f(min_val) == 0:
continue
# 判断区间内是否存在解,若有解加一
if f(min_val) * f(max_val) < 0:
# 逼近于实际值
while max_val - min_val > 1e-5:
mid = (min_val + max_val) / 2
# 若端点积大于0
if f(mid) * f(max_val) > 0:
max_val = mid
else:
min_val = mid
s += 1
if (s == 3)
break;
枚举:
Note: 通过三层升序嵌套循环(其中 j 从 i 开始,k 从 j 开始),当前值的平方和小于输入的 n
函数 寻找四个数(n):
对于 i 从 0 到 满足 sq(i) < n 执行循环:
对于 j 从 i 到 满足 sq(i) + sq(j) < n 执行循环:
对于 k 从 j 到 满足 sq(i) + sq(j) + sq(k) < n 执行循环:
计算 l = sqrt(n - sq(i) - sq(j) - sq(k))
如果 l 是整数:
如果 sq(i) + sq(j) + sq(k) + sq(l) 等于 n:
输出结果 (i, j, k, l)
返回
输出未找到结果
排序:
字典序:
P1012 [NOIP1998 提高组] 拼数( 字典序 )-CSDN博客
Note: 转数字为string,利用字典序判断最大拼数
cmp(){
// 规定字典序顺序
return (a+b)>(b+a)
}
main(){
利用cmp排序数字(转换为string)
返回的拼接后所有数字
}
快排:
Note:选择基准元素,分区左为小于基准,右为大于基准,并递归对子数组进行排序。
分区操作结束时,left
指针的位置实际上是在或右侧的第一个大于或等于基准值的位置
def quicksort(arr, low, high):
if low < high:
# 执行分区操作,找到基准元素的正确位置
pivot_index = partition(arr, low, high)
# 对基准左侧的子数组进行快速排序
quicksort(arr, low, pivot_index - 1)
# 对基准右侧的子数组进行快速排序
quicksort(arr, pivot_index + 1, high)
def partition(arr, low, high):
# 选择最后一个元素作为基准
left = low
right = high - 1
while True:
# 左指针向右移动,直到找到大于等于pivot的元素
# 右指针向左移动,直到找到小于等于pivot的元素
if left < right:
# 如果左指针仍在右指针左侧,交换两个元素
else:
# 否则,分区操作完成
break
# 将基准元素移至正确位置
arr[left], arr[high] = arr[high], arr[left]
return left
桶排序:
Note:
- 数据分布均匀时最为有效。
- 当有合理的方法来选择桶的数量和范围时。
- 对于小范围的整数排序,桶排序非常有效
std::vector<double> bucket_sort(std::vector<double>& arr) {
std::vector<std::vector<double>> buckets(n);
// 将数组中的数分配到各个桶中
// 对每个桶进行排序
// 合并桶中的元素
std::vector<double> result;
return result;
}
计算:
快速幂算法:
Note: 指数奇数时乘底数,指数偶数时乘底数的一半幂
def recursive_quick_power(a, b):
if b == 0:
return 1
if b % 2 == 1:
return (a * recursive_quick_power(a, b - 1))
else:
half = recursive_quick_power(a, b // 2)
return (half * half)
累计加法:
Note: 排序后,计算每个点突出的部分,最后计算下面共有的部分
def calculate_histogram_area(heights):
# 1. 排序柱子
heights.sort()
# 2. 逐步计算突出的部分
for height in heights:
# 计算当前柱子突出的部分并累加到总面积中
total_area += (height - previous_height) * (len(heights) - 1)
previous_height = height
# 3. 计算下方共有的矩形面积
bottom_area = sum(heights) * (heights[-1] - heights[0]) # 下方共有矩形面积
total_area += bottom_area # 加上下方共有的矩形面积
return total_area
倍数判断:
Note: 二维数组 s[len][k] = {0} (用于存储余数出现的次数(代替哈希表))
// len为前数扩展的位数,k为扩展后的取模K
对于 i 从 0 到 n-1:
// 扩展arr[i]数位(0~11)
对于 len 从 0 到 11:
// 更新K
// 更新s[len][K]
对于 i 从 0 到 n-1:
t = arr[i] % k
len = 数字 arr[i] 的位数 (也就是前数需要扩展的位数)
// count加上相补的余数出现次数
count += s[len][(k - t) % k]
// 如果与本身匹配,count减一
输出 count
数学规律:
大数运算:
计算n的n次方的个位数(利用规律图)_求n的n次方的个位数,-CSDN博客
Note: 利用周期为4的规律和N的个位数,计算幂的个位数
def last_digit_of_power(num):
# 计算次方数的周期
# 获取num的个位数
# 如果n的n次方可以被4整除,将n设为4(特殊情况)
# 初始化结果为1
result = 1
# 按周期进行乘法,模拟次方计算
for _ in range(n):
result *= last_digit
# 保证结果为个位数
result %= 10
return result