数学算法思想

算法思想

公式拆分 :  

P9231 [蓝桥杯 2023 省 A] 平方差(拆分问题)-CSDN博客

Note:        得到 x=(y−z)×(y+z),可知 x满足可以拆成 2个奇偶性相同的数的乘积。

可图性判断:

可图性判断(图论)-CSDN博客

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:
                # 如果出现负数,序列不可图

双指针

反向指针(碰撞指针):

反向指针

反向指针是指,双指针(i,j)相向而行,也可记为left与right指针,按照不同的策略进行移动,形成相对应的解题区域。解题思路如下:

初始化两个指针,i=0,j=size-1
While i<=j:
决定该基于nums[i]与nums[j]做出何等操作

在决定后至少移动一个指针(避免死循环)
 模板题:

双指针遍历(找最大值)-CSDN博客

局部最优解导向全局最优解: 每次移动较短的板,尝试找到一个更高的板,这样在减少宽度的同时,有机会增加高度,从而找到一个可能更大的容器。因为如果移动较长的板,高度不会增加,而宽度减少 

移除元素(反向双指针)-CSDN博客 

双指针法移除数组中的指定元素:左指针遍历数组,当遇到要删除的值时,用右指针指向的值替换,然后右指针左移;否则左指针继续前进,最终返回数组中剩余元素的数量。

降维打击(循环定数)

所有和为 0 的不重复三元组:先排序数组,固定一个数,用双指针在剩余部分寻找两个数,使三数之和为 0,跳过重复元素,调整指针确保和为 0 时保存三元组。

15. 三数之和(左右指针)-CSDN博客

 动态维护左右两侧最大高度

 双指针从数组两端向中间移动,动态维护左右两侧的最大高度。每次根据较小的最大高度计算对应指针位置的接水量,并移动该指针,直到左右指针相遇

42. 接雨水(左右指针)-CSDN博客

同向指针(快慢指针)

同向指针是指,双指针(i,j)从相同侧出发,按照不同的策略移动,形成相对应的解题区域。解题思路如下

初始化两个指针,通常均为0
While j<nums.size():
if need nums[j],令nums[i] = nums[j],i++

else j++
模板题:

 删除有序数组中的重复项(同向指针(快慢指针))-CSDN博客

快慢指针来移除数组中的重复元素:快指针遍历数组,慢指针记录不重复元素的位置。当快指针指向的值与慢指针不同,慢指针先移动一位并将快指针的值复制过去。最终,慢指针加1即为不重复元素的个数。 

283. 移动零(快慢指针)-CSDN博客

快慢指针移动零到末尾:如果数组没有0,快慢指针同步移动,元素会被自己复制;如果有0,快指针找到非零元素,将其复制到慢指针位置,最终将剩余位置填充为0。 

前缀和:

1.前缀和 + 滑动窗口 

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);
}

 2.前缀和+哈希表

560. 和为 K 的子数组-CSDN博客 

Note:        使用哈希表记录每个前缀和出现的次数。在遍历数组过程中,查询当前前缀和减去目标值 k 是否存在于哈希表中,若存在,则增加计数。 

        prevs = Hashmap<int , int>
        prevs[0] = 1
        
        sum_ = 0
        count = 0
        for num in nums:
            sum_ += num
            
            if (sum_ - k) in prevs:
                count += prevs[sum_ - k]
            
            prevs[sum_] += 1

素数判断:

欧拉筛法优化(判断素数)-CSDN博客

Note:        利用定理筛选:素数只存在6的倍数附近(即6k+1,6k-1)

                 再用遍历范围5~sqrt(num),+=6验证

二分查找:

      求方程单解:

二分查找算法-CSDN博客

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

方程多解: 

二分查找,求方程多解-CSDN博客

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;

枚举:

枚举算法思想-CSDN博客

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)
     
    返回的拼接后所有数字
}

快排:

快速排序(按从小到大排)-CSDN博客

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

桶排序:

(桶排序)-CSDN博客

Note:         

  • 数据分布均匀时最为有效。
  • 当有合理的方法来选择桶的数量和范围时。
  • 对于小范围的整数排序,桶排序非常有效
std::vector<double> bucket_sort(std::vector<double>& arr) {
    std::vector<std::vector<double>> buckets(n);
    
    // 将数组中的数分配到各个桶中
    
    // 对每个桶进行排序

    // 合并桶中的元素
    std::vector<double> result;
    
    return result;
}

计算:

快速幂算法:

快速幂算法(数论)-CSDN博客

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)

 累计加法: 

累计加法(数学)-CSDN博客

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

倍数判断: 

K倍判断(巧用大整数取模定理)-CSDN博客

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值