zoukankan      html  css  js  c++  java
  • 解题营_算法

    基础算法

    前言

    听完课然后来补题

    空下没有写的题大概率是不会写了


    枚举

    • 输出 ([l, r]) 之间的所有整数
    • 生成全排列
    • 输出给定集合的所有子集
    • 输出给定集合的所有子集以及每个子集的所有子集
    • 在棋盘上枚举八个方向
    • 输出给定值的所有约束

    暴力

    作为部分分的做法


    Meet in the middle

    有四个整数数列 每个整数数列包含 (n) 个整数 现在从四个整数数列中各选一个数 使得四个数的和为 (0) 求有多少种选法
    只要选择的数在不同的位置 就算做不同的选法
    (n leq 1000)

    直接做 (n^4)

    和为 (0)

    枚举两个序列 从两个序列中得到两两配对的数组 再在两个序列中枚举和为 (0)

    总复杂度 (O(n^2))


    题目

    (n) 个数字 找出两两和的第 (k)
    (n leq 10^6)

    小于 (x) 的数字有恰好 (k - 1)

    先排序 使序列有序

    对于一个数 (y) 先找 (y) 然后找 (x - y) 可以二分


    题目

    给出数列 (a, b) 长度为 (n, m)(c_{i, j} = a_i imes b_j) 中的第 (k) 大的数


    题目

    (n) 个数字 找出 (2^n) 个子集和里的第 (k)
    (n leq 20)
    (n leq 30)

    折半搜

    将一个集合分为两个 再考虑两两之间的关系 降低复杂度


    CodeChef LEBOXES

    (n) 个箱子 每个箱子打开有 (p_i) 的概率获得 (v_i) 的金币 (1 - p_i) 的概率获得 (1) 个钻石

    这个人开完箱子后 会拿这些金币和钻石去买东西 (m) 个东西 第 (i) 个东西需要 (C_i) 金币 (D_i) 钻石购买 问购买的东西数量的最大值的期望

    (2 leq n leq 30, 1 leq m leq 30, 1 leq v_i, c_j leq 10^7, 0 leq d_j leq 30, 0 leq p_i leq 100)

    金币很多 钻石很少 物品的数量也很少 背包计算使用 (i) 个钻石时 买 (j) 个物品需要的金币数 将箱子分为两部分 预处理第一部分(钻石数 金币数 概率)三元组 与第二部分合并

    直接枚举箱子的状态 (2^{15}) 合并时枚举其中一个 在另一个里面判断是否合法


    分治

    分而治之

    将一个复杂的问题分为两个或更多相同或相似的子问题 在把子问题分成更小的子问题 直到最后子问题可以简单的直接求解 再合并得到原问题的解


    归并

    快速幂 龟速乘

    二分查找 二分答案

    分治


    P2759 奇怪的函数

    相当于求

    [x^x geq 10^{n - 1} ]

    直接化式子 因为这 (x) 是大于等于 (1) 的 所以不会有什么问题

    [x^x geq 10^{n - 1} \ x geq log_x10^{n - 1}\ x geq (n - 1)log_x10\ x geq frac {n - 1}{lg x}\ xlg x geq n - 1\ x frac {log_2x}{log_210} geq n - 1 ]

    然后左边的东西是个单调的 直接二分就可以了


    代码


    P1258 小车问题

    设甲乘车走 (x) 则其步行走 (s - x) 不妨设车先去接甲 那么甲下车后步行的时间就是车返程接乙以及送乙到终点的时间 故 有:

    [frac {s - x}a = frac {s - 2(s - x) + x}b\ frac {s - x}a = frac {3x - s}b\ x = frac {sb + as}{3a + b} ]

    [ans = frac {s - x}a + frac xb ]


    代码


    P1429 最近点对

    玄学旋转 按照横坐标排序后再考虑最近的几个点就能通过

    既然能过我写正解干啥


    代码


    正解为分治

    将整个图取中线分为左右两个 找出左右两边的最小距离 再计算跨越中线但是距离不超过左右两边最小距离的较小距离的点对 记录最小值 递归处理


    代码


    分数规划

    求一个分式的极值

    给定 (a_i, b_i) 求一组 (w_i in {0, 1}) 最大化或最小化

    [frac {sum_{i = 1}^na_i imes w_i}{sum_{i = 1}^nb_i imes w_i} ]


    通用求解方法 二分求解

    假设求最大值 二分一个 (mid) 然后化

    [frac {sum a_i imes w_i}{sum b_i imes w_i} > mid ]

    移项

    [sum a_i imes w_i - mid imes sum b_i imes w_i > 0 ]

    [sum w_i imes (a_i - mid imes b_i) > 0 ]

    那么只要求出不等号左边的式子的最大值 看一下是否大于 (0) 就可以了

    最小值类似


    题目

    给定 (n) 个物品 每个物品两个权值 选择 (n - k)(p_1, p_2, dots, p_{n - k}) 使 (frac{sum a_{p_i}}{sum b_{p_i}}) 最大

    二分答案 将第 (i) 个物品的权值设为 (a_i - mid imes b_i) 然后贪心的取即可


    题目

    在上面的题目的基础上加上一条限制:

    给定 (W) 使 (sum b_{p_i} geq W)

    多了 (W) 无法直接贪心

    考虑 (01) 背包 将 (b_i) 作为第 (i) 个物品的重量 (a_i - mid imes b_i) 作为第 (i) 个物品的价值 然后做背包即可

    (f_{n, W}) 即为最大值


    最优比例生成环

    在一个 (n) 个点 (m) 条边的无向图中找一个环 使这个环的点权和除以边权和最大 假设所有的权值都为整数

    (a_i - mid imes b_i) 作为边权 看是否有正环


    最优比例生成树

    (n) 个村庄 村庄在不同坐标和海拔 现在要对所有村庄供水 只要两个村庄之间有一条道路即可 建造水管距离为坐标之间的欧几里得距离 费用为海拔之差现在要求方案使得费用与距离的比值最小

    (a_i - mid imes b_i) 作为每一条边的权值 求最小生成树


    扩展 乘积规划(未补)


    P2571 传送带

    三分套三分


    首先 在 (AB) 上取一点 (E)(CD) 上取一点 (F)

    [ans = frac {dis_{A, E}}P + frac {dis_{E, F}}R + frac {dis_{F, D}}Q ]

    二元函数求最小值

    若可以确定 (E) 点 则第一项为定值 后两项为一个单峰函数 可以三分确定最值 把后面两项看为整体 也是一个单峰函数 三分 (E) 的位置


    代码


    poj 1747 P4178

    给定 (n) 个节点的数 边带权 给出一个 (k) 询问有多少个数对 ((i, j)) 满足 (i < j)(i)(j) 两点在树上的距离小于等于 (k)

    点分治


    显然可以 (O(n^2)) 显然可以 (TLE)

    显然可以点分治 显然这是个模板题

    复杂度 (O(n log^2 n))


    COGS 577 蝗灾

    CDQ 分治


    贪心

    贪心算法指从问题的初始状态出发 通过若干次的贪心选择而得出最优值的一种解题方法

    贪心思想的本质是每次都形成局部最优解 换一种方法说 就是每次都处理出一个最好的方案

    贪心算法并不总是正确的 因为并不是每次局部最优解都会与整体最优解之间有联系 往往靠贪心生成的最优解不是最优解


    合并果子/huffman 树

    huffman 树

    哈夫曼树一般是二叉树 建树的方法就是每次选择两个权值最小的点 删除这两个点 加入一个权值是这两个点之和的新点进去 并且使这被删除的两个点的父亲成为那个新点

    具体而言 将 (n) 堆果子看做 (n) 个数字加入一个小根堆 每次合并相当于连续两次去除堆顶元素 将取出的两个树求和 再次加入堆中


    荷马史诗

    (k) 叉哈夫曼树编码


    代码


    区间选择

    给定 (n) 个形如 ([a_i, b_i]) 的区间 问最多能选出多少个不相交的区间

    线段覆盖问题...

    按照右端点排序即可

    代码略过


    区间点覆盖

    数轴上有 (n) 个闭区间 ([a_i, b_i]) 取尽量少的点 使得每个区间内都有至少一个点

    按照右端点直接往最后插即可

    代码略过


    P4025

    一个讲了数遍的题

    每次讲贪心都有这个题

    代码略过


    P3620 数据备份

    反悔贪心

    可以发现所选的匹配一定是相邻的两个点 将两点之间的距离看做新的点的点权 问题相当于选择一个点不能选择相邻的点

    与种树那个题的贪心策略相同 链表 + 堆


    代码


    搜索

    在状态空间上按照某种顺序枚举状态


    剪枝

    最优性剪枝: 在当前搜索出的一组解的情况下 对现在的状态进行评估 如果这个状态按照最理想的情况也不能比当前最优解优 则放弃这个状态 也可以在已经搜索过"相同"状态时进行剪枝

    可行性剪枝: 在当前状态不合法时进行剪枝


    生日蛋糕

    搜索顺序

    剪枝

    从下往上搜

    最优性剪枝: 已经算得的答案减去蛋糕当前层一下的层的总面积是否小于上面的层所能构成的最小面积 如果小于则返回

    可行性剪枝: 总体积减去蛋糕当前层一下的层的总体积是否小于上面的层所能构成的最小体积 如果小于则返回


    bzoj 1085 骑士精神

    (IDA^*)


    代码


    其他练习题

    P3397 地毯

    显然可以二维差分直接碾过去


    P1842

    先钱后时间 从后往前倒着贪即可


    P1230

    结论比较好推 细节比较恶心


    显然对于第 (i) 头牛 其对答案贡献与其上的所有牛的排列顺序无关

    设其上所有牛总重为 (sum)

    (i) 先于 (j) 有:

    [ans = max(sum - s_i, sum + w_i - s_j) ]

    (j) 先于 (i) 有:

    [ans = max(sum - s_j, sum + w_j - s_i) ]

    (i) 先于 (j) 比较优时 有:

    [max(sum - s_i, sum + w_i - s_j) < max(sum - s_j, sum + w_j - s_i) ]

    其中 (sum - s_i)(sum - s_j) 两项无论如何不会成为最大值 故 有:

    [sum + w_i - s_j < sum + w_j - s_i ]

    化简 有:

    [w_i + s_i < w_j + s_j ]

    即为贪心策略


    注意不需要特判第一头牛 其压扁指数按照定义算就可以了(负数)


    代码


    P4053

    反悔型贪心


    按照最后时间排序 对每个先选上 时间不够了考虑找之前花费时间最长的那个 如果其花费的时间比当前正考虑的时间长 把那个吐出来 选上当前这个


    代码


    ——END

  • 相关阅读:
    Firebird 用查询结果集更新数据,merge
    wcf 登录认证 angular 认证重定向
    WOSA/XFS PTR Form解析库—xfsptrdata.h
    WOSA/XFS PTR Form解析库—头文件
    2019,开启新征程
    VC中添加头文件以及库
    .net4.0多进程间共享内存实现通信(VB.Net)
    共享内存操作类(C#源码)
    VC++ 共享内存读写操作
    c#实现内存映射文件共享内存
  • 原文地址:https://www.cnblogs.com/blank-space-/p/15142113.html
Copyright © 2011-2022 走看看