SDU泰山学堂计算机方向算法复习&ACM复习

Preface

嘛泰堂没进 所以是 acm 复习了

排序

参考文章

alt text

均以升序排列为例

No.1 冒泡排序

简单直接的一个排序算法

把相邻元素进行比较,最大的往后换,每轮的最后一个位置的数一定是该轮中最大的

时间复杂度 O(n2)

代码实现

#include <bits/stdc++.h>

#define N 500005
#define re register int
#define Endl cout << '\n'

using namespace std;

void Fastio_setup() { ios :: sync_with_stdio(false) ; cin.tie(NULL), cout.tie(NULL); }

int a[N];

inline void sorting(int tmp[], int tmplen) // 升序排列
{
    for (re i = tmplen ; i >= 2 ; -- i) // 排序序列的长度递减,确保次大元素能够移动到对应位置
        for (re j = 1 ; j <= i-1 ; ++ j) // 每个元素依次与下一个元素相比
            if (tmp[j] > tmp[j+1])
                swap(tmp[j], tmp[j+1]);
}

void work()
{
    cin >> a[0];
    for (re i = 1 ; i <= a[0] ; ++ i)
        cin >> a[i];
    
    sorting(a, a[0]);
    
    Endl;
    for (re i = 1 ; i <= a[0] ; ++ i)
        cout << a[i] << ' ';
    Endl;
}

int main()
{
    Fastio_setup();
    work();
    return 0;
}

No.2 选择排序

也是非常的直观,从序列中扫描出最大值,和数组末端互换,再对剩下的元素扫描重复步骤

时间复杂度 O(n2)

代码实现

#include <bits/stdc++.h>

#define N 500005
#define re register int
#define Endl cout << '\n'

using namespace std;

void Fastio_setup() { ios :: sync_with_stdio(false) ; cin.tie(NULL), cout.tie(NULL); }

int a[N];

inline void sorting(int tmp[], int tmplen) // 升序排列
{
    int maxx = -0x7fffffff, targ = -0x7fffffff;
    for (re i = tmplen ; i >= 2 ; -- i) // 本次扫描长度
    {
        maxx = -0x7fffffff;
        for (re j = 1 ; j <= i ; ++ j)
            if (maxx < a[j])
                maxx = a[j], targ = j;
        swap(a[i], a[targ]);
    }
}

void work()
{
    cin >> a[0];
    for (re i = 1 ; i <= a[0] ; ++ i)
        cin >> a[i];

    sorting(a, a[0]);

    Endl;
    for (re i = 1 ; i <= a[0] ; ++ i)
        cout << a[i] << ' ';
    Endl;
}

int main()
{
    Fastio_setup();
    work();
    return 0;
}

No.3 插入排序

这个算法对于上述两个稳定 O(n2) 的算法有所不同,在不同数据下表现不同,上限是 O(n2),在极特殊条件下(序列原本就有序)直接做到了 O(n)

算法思想:将数组最左端的元素视为有序序列,每次向右拓展一个,向右拓展的该元素与有序序列的元素从右往左依次比较,将其插入到合适的位置,有序序列因此扩增,循环往复直至全体序列有序

由于大多数情况下序列都是无序的而且构造在大数据规模下都“差不多”的,所以认为他的时间复杂度仍然为 O(n2)

代码实现

#include <bits/stdc++.h>

#define N 500005
#define re register int
#define Endl cout << '\n'

using namespace std;

void Fastio_setup() { ios :: sync_with_stdio(false) ; cin.tie(NULL), cout.tie(NULL); }

int a[N];

inline void sorting(int tmp[], int tmplen) // 升序排列
{
    int val = 1;
    for (re i = 2 ; i <= tmplen ; ++ i) // 有序序列长度+1
    {
        val = tmp[i];// 待插入元素
        for (re j = i-1 ; j >= 1 ; -- j) // 有序序列从后向前扫描
        {
            if (val >= tmp[j])
                { tmp[j+1] = val; break; }
            else
                tmp[j+1] = tmp[j];
        }
    }
}

void work()
{
    cin >> a[0];
    for (re i = 1 ; i <= a[0] ; ++ i)
        cin >> a[i];

    sorting(a, a[0]);

    Endl;
    for (re i = 1 ; i <= a[0] ; ++ i)
        cout << a[i] << ' ';
    Endl;
}

int main()
{
    Fastio_setup();
    work();
    return 0;
}


博弈

图论

Dijkstra

这玩意以前好像就没弄明白原理

重新看一遍博客感觉还是有点怪(为啥遍历一遍就能全min了)

不过差不多知道原理了

先说 O(n2)

从离源点最近的点 u,然后遍历他的每个出边更新 vdis

在整个dis里找到最小值作为新的 u

u 不会超过 n 次所以索性直接循环 n

这就是朴素的 Dijkstra

堆优化是把找 dis 的过程用优先队列优化掉

这样的复杂度就是 O(nlogn)

你可能会问为什么不在更新的时候直接统计 v 达到 O(n+m) 的复杂度,我试了,大概是这样实现:

code
int u = 1, minn = 999999999;
    for (int i = 1 ; i <= n ; ++ i) {
        for (int j = head[u] ; j ; j = e[j].nxt) {
            int v = e[j].v;
            if (dis[v] > dis[u] + e[j].w)
                dis[v] = dis[u] + e[j].w;
        }
        minn = 999999999;
        for (int j = head[u] ; j ; j = e[j].nxt) {
            if (dis[v] < minn) {
                u = v;
                minn = dis[v];
            }
        }
    }

因为在上一轮可能有两个比较小的 v,这次再扫的新的点里可能不包含上一轮的次小 v,导致漏掉点。

堆优化:

code
void Dijkstra() {
    memset(dis, 0x7f, sizeof(dis));
    // cerr << dis[0] << '\n';
    dis[st] = 0; q.push((node){0, st});

    while (q.empty() == false) {
        node tmp = q.top(); q.pop();
        if (vis[tmp.pos] == false) { // 以此点向外拓展
            vis[tmp.pos] = true;
            for (int i = head[tmp.pos] ; i ; i = e[i].nxt) {
                if (dis[e[i].v] > dis[tmp.pos] + e[i].w) {
                    dis[e[i].v] = dis[tmp.pos] + e[i].w;
                    q.push((node){dis[e[i].v], e[i].v});
                }
            }
        }
    }
}

SPFA

数据结构

No.1 线段树

洛谷P3372板子题(要lazytag)

代码实现
#include <iostream>
#include <cstdio>
#include <map>
#include <cstring>

#define MAX(a, b) (((a) > (b)) ? (a) : (b))
#define MIN(a, b) (((a) > (b)) ? (a) : (b))
#define Endl cout << '\n'
#define _ ' '
#define N 100005

using namespace std;

void Fastio_setup() { ios::sync_with_stdio(false); cin.tie(NULL), cout.tie(NULL); }

/*
    浅复习一下线段树
    写对了,但是估计需要加lazytag
    我想想咋写的
*/

int n, Q;
long long a[N];

struct Segment_Tree
{
    struct node { long long sum, lz; };

    struct node tr[N << 2];

    #define lson (rt << 1)
    #define rson (rt << 1 | 1)
    #define mid ((l + r) >> 1)

    inline void Pushup(int rt, int l, int r) { tr[rt].sum = tr[lson].sum + tr[rson].sum; }// + tr[lson].lz*(mid-l+1) + tr[rson].lz*(r-mid); }
    /*inline void Pushdown(int rt)
    {
        tr[lson].lz = tr[rt].lz;
        tr[rson].lz = tr[rt].lz;

    }*/// 算了一会再写lazytag的
    inline void Pushdown(int rt, int l, int r)
    {
        // tr[lson].sum += tr[lson].lz * (mid-l+1);
        // tr[lson].lz = tr[rt].lz;
        // tr[rson].sum += tr[rson].lz * (r-mid);
        // tr[rson].lz = tr[rt].lz;
        // Pushup(rt, l, r); tr[rt].lz = 0;// 一会会Pushup的
        tr[lson].lz += tr[rt].lz;
        tr[rson].lz += tr[rt].lz;
        tr[lson].sum += tr[rt].lz * (mid-l+1);
        tr[rson].sum += tr[rt].lz * (r-mid);// lz不会上传,upd时候没有给儿子更改lz因此现在加是真的
        tr[rt].lz = 0;
    }

    void Build(int rt, int l, int r)
    {
        if (l == r)
            { tr[rt].sum = a[l]; return ; }
        Build(lson, l, mid);
        Build(rson, mid+1, r);
        Pushup(rt, l, r);
    }

    void Update(int rt, int l, int r, int L, int R, long long val)
    {
        if (L <= l and r <= R)
            { tr[rt].lz += val; tr[rt].sum += val * (r-l+1); return ; } // 先不乘
        if (tr[rt].lz != 0)
            Pushdown(rt, l, r);
        if (L <= mid)
            Update(lson, l, mid, L, R, val);
        if (R > mid)
            Update(rson, mid+1, r, L, R, val);
        Pushup(rt, l, r);
    }

    long long Query(int rt, int l, int r, int L, int R)
    {
        if (L <= l and r <= R)
            { return tr[rt].sum; }
        if (tr[rt].lz != 0)
            Pushdown(rt, l, r);
        // Pushup(rt, l, r); // 呃
        if (R <= mid)
            return Query(lson, l, mid, L, R);
        else if (L > mid)
            return Query(rson, mid+1, r, L, R);
        else 
            return (Query(lson, l, mid, L, R) + Query(rson, mid+1, r, L, R));
    }

    #undef lson
    #undef rson
    #undef mid
};

struct Segment_Tree Segment;

void work()
{
    cin >> n >> Q;
    for (int i = 1 ; i <= n ; ++ i)
        cin >> a[i];
    Segment.Build(1, 1, n);
    int opt, x, y; long long k;
    while (Q --)
    {
        cin >> opt;
        if (opt == 1)
        {
            cin >> x >> y >> k;
            Segment.Update(1, 1, n, x, y, k);
        }
        else 
        {
            cin >> x >> y;
            cout << Segment.Query(1, 1, n, x, y) << '\n';
        }
    }
}

int main() {
    // freopen("T.in", "r", stdin);
    // freopen("T.out", "w", stdout);
    Fastio_setup();
    work();
    return 0;
}

笛卡尔树

icpc 第一场网络预选赛考到了导致我强行学习

参考博客

原来是个二叉树+堆啊

做题时候想到了所以终于印象深刻

对于任一个节点 x,满足性质:

  • id[lson] <= id[x] <= id[rson]

  • val[x] > val[lson], val[rson]

由于pos是连续的所以中序遍历是原代号(任意x的子树中)

posted @   char_phi  阅读(7)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示