2024nomomo冬令营算法笔记

谢谢wls老师,讲的真的非常好

Day1 动态规划

1.sum equals

f(i,j) g(i,j)表示a,b数组定下前i个数的取值使得总的和为j的方案数,发现复杂度过大。

优化为f(i,j,k)表示a取前i个数,b取前j个数,a、b的差值为k的方案数。因为我们最终要求的答案是对于任意的i任意的j,k=0的方案数,根据“水多加面,面多加水”的思想,当k>0时我们去取下一个b,当k<=0时我们去取下一个a,可以将第三维的复杂度控制在1000以内,最终复杂度为(n+m)*1000

2.某数据范围超大的01背包题

有n个物品,其中第i个物体的权值为ai,要求从中选出一些物体,使得权值和<=c并且最大。

n,ai<=2e4,c<=1e9

  1. 先贪心地选出一组 <=c 的解y
  2. 正解x  - y 必定小于2e4 ,否则y干嘛不直接再选一个ai
  3. 考虑如何将这个y-->x, 我们将贪心拿的东西变成负值,没拿的东西不变。
  4. 把正数放到上面,把负数放下面,用f[i][j][k]表示用了上面前i个数,下面前j个数,能不能表达k。
  5. 此时空间复杂度过大,我们考虑优化,最后一维肯定不能优化,考虑优化前两维。
  6. 考虑把j优化,用g[i][k]表示上面用了i个数,为了表达k,下面最少用前几个数。
  7. 两种转移:第一种看i+1取不取。第二种看g[i][k]后面取哪一个,只能看g[i][k]+1~g[i-1][k](上面取i-1个,下面取g[i-1][k]个就能表达k。那么当上面的选择变多时,下面的选择最差的情况也肯定大于g[i][k]。k固定的时候,i变大意味着前面能多几种选择。那么最差的情况,数组里面的值也是和i变大前一样)
  8. 时间复杂度o(na)

3.skills

  1. 在最优解⾥⾯,⼀个技能如果开始学习了,那么中间过程⼀定不会减到 0 以下,否则不如学习别的技 能。所以⼀个技能开始学习了就可以忽略不能减到 0 以下的限制,不学习就扣出没学习的天数,否则不算分数。
  2. 然后注意到⼀个技能开始学习了,不会停⽌学习超过2\sqrt{W}+O(1) 天。⽐如⼀个技能 200 天没有学习,不如 100 天的时候学⼀下,那么会损失掉当天的收益,但是后⾯ 100 天每天会少损失 100 点,所以不亏。
  3. 然后可以⽤动态规划f(i,j1,j2,j3) 表示第i 天,三个技能分别有 j 天没有学习,其中 0 表示从未开始 学习过,不需要算分数。 ⾥⾯⾄少有⼀个是 1。

4.Time Limit Exceeded

二维前缀和-->三维前缀和-->多维前缀和-->0/1-->2^n-->子集dp

  1. 我们用dp[i]表示以i为结尾的方案数,
  2. 我们发现要满足a[i]&a[i+1]=0,则dp[i]是从上一次结果中所有满足i&j=0的地方转移过来的i&j=0即i&(~j)=i,即i为~j的子集,那么我们每次对上一次的结果进行下标取反操作,
  3. 那么求当前dp[i],就是求出以i为子集的上一次计算出的dp值的高维前缀和。
  4. 对于c[i]这个条件,我们每轮计算后将c[i]倍数为下标的dp值置0即可。
#include <cstdio>
#include <algorithm>
#include <cstring>

using namespace std;
int T, n, m, c[100];
const int mod = 1000000000;

struct data {
    int val;
    data operator+(const data &rhs) const {
        int t_val = val + rhs.val;
        if (t_val >= mod)t_val -= mod;
        if (t_val < 0)t_val += mod;
        return data{t_val};
    }
} dp[(1 << 15) + 10], res;

int main() {
    scanf("%d", &T);
    while (T--) {
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= n; i++)scanf("%d", &c[i]);
        int all = (1 << m);
        for (int j = 0; j < all; j++)dp[j].val = (j == 0);
        for (int i = 1; i <= n; i++) {
            for (int j = 0; j < all; j += 2)swap(dp[j], dp[j ^ (all - 1)]);
            for (int j = 0; j < m; j++)
                for (int k = 0; k < all; k++) {
                    if (~k & (1 << j))dp[k] = dp[k] + dp[k | (1 << j)];
                }
            for (int j = 0; j < all; j += c[i])dp[j].val = 0;
        }
        res.val = 0;
        for (int j = 0; j < all; j++)res = res + dp[j];
        printf("%d\n", res.val);
    }
    return 0;
}

5. Jzzhu and Numbers

容斥+高维前缀和

令g[i]表示i的二进制表示中有几个1,f[i]表示有多少个j满足i&a[j]=i,故答案为:

\sum_{i=0}^{2^{20}}(-1)^{g[i]}2^{f[i]}

6.方差

好题,思想很多。尤其是把“将ai换成a[i-1]+a[i+1]-a[i]”看出"d[i]与d[i+1]交换”的思想很妙。

以及要发现方差的一些性质,什么单谷效应啊...

累了(偷懒链接)

计算几何不是我的长项...交给未来的队友!

Day2 计算几何

凸包

  1. 任何一个套住所有点的直线“边框”上的点一定可以在凸包上。 因此,我们可以任取 x = min x, x = max x, y = min y, y = max y 上的点开始构造凸包。(即这四点必然在凸包上)
  2. 扫描线法:极角序(Graham):
    选取左下角的点作为基准,对其余点进行逆时针排序。
    用一根细绳尝试绕过按照这个顺序绕过所有点。不是凸包的点自然弹出。
  3. 扫描线法:字典序(Andrew):扫描两遍计算出上下凸壳,通常在效率和实现上相比 Graham 有优势。​​​​​​​

Convex Checker

按照顺序给点集P,问该点集构成的图形是否为凸包。

  1. 检查所有内角是否为劣角(∠p1-p2-p3 ,∠p2-p3-p4.... ) 并且 检查内角和是否为 ( n 2) π (比如五角星不是凸包)
  2. 运行凸包算法,检查凸包是否一致。 (将凸包算出来的向量集合与题目给的点集构成的向量集合相比较)
  3. 魔改 Graham 算法:将左下角的点转到开头,按顺序检查极角序和凸性。 ​​​​​​​

小几何 - Virtual Judge (vjudge.net)

  • 16
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值