ACWing 算法基础课 习题课

第k个数(快速排序每次只需要递归区间的一半) 


找出一个分界点x使得左边都<=x, 右边都>=x统计左右两边区间中数的个数分别记为SL, SK。
1、当k<=SL时,第k个数一定在左边,递归处理左边区间
2、当k>SL时,第k个数一定在右边,递归处理右边区间,整个区间第K小的数即为右边区间k-SL个数
时间复杂度为O(n)每次都处理区间一半,快速选择算法,保证第K个数一定在区间里面  

#include <iostream>

using namespace std;

const int N = 100010;

int q[N];//总的数 

int quick_sort(int q[], int l, int r, int k)
{
    if (l >= r) return q[l];//保证k在区间里面 

    int i = l - 1, j = r + 1, x = q[l + r >> 1];
    while (i < j)
    {
        do i ++ ; while (q[i] < x);// while(q[--i] < x) 
        do j -- ; while (q[j] > x);// while(q[++j] > x)
        if (i < j) swap(q[i], q[j]);
    }

    if (j - l + 1 >= k) return quick_sort(q, l, j, k);//j - l + 1为左边区间的数的总数 
    else return quick_sort(q, j + 1, r, k - (j - l + 1));
}//模板 

int main()
{
    int n, k;
    scanf("%d%d", &n, &k);//排序总数和找到的第k个数 

    for (int i = 0; i < n; i ++ ) scanf("%d", &q[i]);

    cout << quick_sort(q, 0, n - 1, k) << endl;

    return 0;
}
/*递归结束ij只有以下两种情况:
1、i=j 2、i = j + 1*/ 


逆序对的数量


逆序对:从所有数中暗器那后顺序选两个数,前面的数比后面的数大即为逆序对 
归并排序流程:
1、[L, R] => [L, mind], [mid + 1, R]
2、递归排序[L, mid]和 [mid + 1, R]
3、归并,将左右两个有序序列合并成一个有序序列 
求逆序对思想: 
1、基于分治的思想将两个逆序对分成三种情况,两个数同时出现在右半边,左半边,一边一个 
(1)左半边内部的逆序对数量:merge_sort(L, mid) 
(2)右半边内部的逆序对数量:merge_sort(mid + 1, R)
(3)统计左半边里面有多少个数比右半边的数大,用两个指针分别指向左半边和右半边,找出比q[j]大的q[i]后每次输出q[j]都加上mid - j + 1 

#include <iostream>

using namespace std;

typedef long long LL;

const int N = 1e5 + 10;

int a[N], tmp[N];//tmp临时数组用来存储中间排序的结果 

LL merge_sort(int q[], int l, int r)
{
    if (l >= r) return 0;//l = r类似 

    int mid = l + r >> 1;//+优先级比>>高故不需要加(),该表达式等价于(l + r) / 2 

    LL res = merge_sort(q, l, mid) + merge_sort(q, mid + 1, r); //求左半边和右半边逆序对的个数 
    
    //归并的过程,计算第三种情况下逆序对的个数 
    int k = 0, i = l, j = mid + 1;
    while (i <= mid && j <= r)
        if (q[i] <= q[j]) tmp[k ++ ] = q[i ++ ];
        else
        {
            res += mid - i + 1;
            tmp[k ++ ] = q[j ++ ];
        }
    //扫尾(最多只进行一个循环,数组有一部分已经没有数了 
    while (i <= mid) tmp[k ++ ] = q[i ++ ];
    while (j <= r) tmp[k ++ ] = q[j ++ ];
    
    //将临时数组中的数据返回给原来的数组 
    for (i = l, j = 0; i <= r; i ++, j ++ ) q[i] = tmp[j];

    return res;
}

int main()
{
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; i ++ ) scanf("%d", &a[i]);

    cout << merge_sort(a, 0, n - 1) << endl;

    return 0;
}

数的三次方根(浮点数的二分)


首先确定区间 0 ~ max(1, x),答案必须在区间里面,取m = (l + r) / 2如果m的三次方大于等于x,则x的三次方根在左半边区间更新区间r = m
否则更新区间l = m 

#include <iostream>

using namespace std;

int main()
{
    double x;
    cin >> x;

    double l = -10000, r = 10000;
    while (r - l > 1e-8)
    {
        double mid = (l + r) / 2;
        if (mid * mid * mid >= x) r = mid;//左半边区间 
        else l = mid;
    }

    printf("%.6lf\n", l);
    return 0;
}


前缀和


一维前缀和:
核心公式 
    1.Si = a1 + a2 + ... + ai 
    2.sum(L, R) = aL + aL + 1 + aL + 2 + ... + aR = SR - SL-1
方法:
1.预处理前缀和数组 
2.用公式求区间和 
 

#include <iostream>

using namespace std;

const int N = 100010;

int n, m;
int a[N], s[N];//a原数组,s前缀和数组 

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i ++ ) scanf("%d", &a[i]);//i = 1,保证计算前缀和时下标不越界 

    //求前缀和,凡是下标中出现减一的情况在循环时循环变量都是从1开始 
    for (int i = 1; i <= n; i ++ ) s[i] = s[i - 1] + a[i]; 

    while (m -- )
    {
        int l, r;
        scanf("%d%d", &l, &r);
        printf("%d\n", s[r] - s[l - 1]); // 区间和的计算
    }

    return 0;
}

子矩阵的和(二位前缀和) 


S[i,j]为ij左上角格子的和 
核心操作:
1、计算(x1,y1)(x2,y2)这一子矩阵中所有数的和 S[x2,y2] - S[x1 - 1,y2] - s[x2, y1 - 1] + S[x1 -1,y1 - 1] 
2、计算S[i,j]按行进行计算 S[i,j] = S[i - 1,j] + S[i, j - 1] - S[i - 1, j - 1] + a[i,j] 

#include <iostream>

using namespace std;

const int N = 1010;

int n, m, q;
int s[N][N];

int main()
{
    scanf("%d%d%d", &n, &m, &q);

    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= m; j ++ )
            scanf("%d", &s[i][j]);

    //初始化前缀和数组 
    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= m; j ++ )
            s[i][j] += s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1];

    while (q -- )
    {
        int x1, y1, x2, y2;
        scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
        printf("%d\n", s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1]);
    }

    return 0;
}

差分(一维差分)


给定a[1],a[2]...a[n]数组构造差分数组b[N]使得a[i] = b[1] + b[2] + ... + b[i]
核心操作:首先先将a数组全部看成为零,再将a[L ~ R]全部加上C, 等价于:b[L] += C, b[R + 1] -= C
该操作影响:1、a[1 ~ L - 1]无影响 2、a[L ~ R]加上了C 3、a[R + 1 ~ N]无影响作用将O(n)的时间复杂度变为O(1) 
即在全是零的数组上进行n步的插入操作 

#include <iostream>

using namespace std;

const int N = 100010;

int n, m;
int a[N], b[N];

void insert(int l, int r, int c)
{
    b[l] += c;
    b[r + 1] -= c;
}

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i ++ ) scanf("%d", &a[i]);

    for (int i = 1; i <= n; i ++ ) insert(i, i, a[i]);

    while (m -- )
    {
        int l, r, c;
        scanf("%d%d%d", &l, &r, &c);
        insert(l, r, c);
    }

    for (int i = 1; i <= n; i ++ ) a[i] = a[i - 1] + b[i];

    for (int i = 1; i <= n; i ++ ) printf("%d ", a[i]);

    return 0;
}


差分矩阵(二位差分) 


给定原矩阵a[i,j],构造差分矩阵b[i,j],使得a[][]是b[][]的二位前缀和
差分核心操作(插入):给以(x1, y1)为左上角,(x2, y2)为右下角的子矩阵中的所有数a[i,j]都加上C
对于差分数组b的影响:(两重循环改变为只改变四个数)
b[x1, y1] += C b[x1, y2 + 1] -= C b[x2 + 1, y1] -=C b[x2 + 1, y2 + 1] += C 

#include <iostream>

using namespace std;

const int N = 1010;

int n, m, q;
int a[N][N], b[N][N];

void insert(int x1, int y1, int x2, int y2, int c)
{
    b[x1][y1] += c;
    b[x2 + 1][y1] -= c;
    b[x1][y2 + 1] -= c;
    b[x2 + 1][y2 + 1] += c;
}

int main()
{
    scanf("%d%d%d", &n, &m, &q);

    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= m; j ++ )
            scanf("%d", &a[i][j]);

    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= m; j ++ )
            insert(i, j, i, j, a[i][j]);

    while (q -- )
    {
        int x1, y1, x2, y2, c;
        cin >> x1 >> y1 >> x2 >> y2 >> c;
        insert(x1, y1, x2, y2, c);
    }

    
    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= m; j ++ )
            a[i][j] = a[i - 1][j] + a[i][j - 1] - a[i - 1][j - 1] + b[i][j];

    for (int i = 1; i <= n; i ++ )
    {
        for (int j = 1; j <= m; j ++ ) printf("%d ", a[i][j]);
        puts("");
    }

    return 0;
}

代码转载于

作者:yxc
链接:https://www.acwing.com/activity/content/code/content/39800/
来源:AcWing 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: acwing算法基础课是一门针对算法学习的在线课程,在这门课程中,学生可以系统地学习和掌握算法基础知识,提高编程水平。为了方便学生学习,acwing提供了网盘服务。 acwing算法基础课网盘是一个用于存储课程资源的平台。通过这个网盘,学生可以下载课程讲义、代码模板以及补充材料等。这些资源都经过精心整理,供学生们参考和学习。 网盘中的资源是按照课程章节进行分类的,学生可以根据自己的学习需要,选择性地下载所需的资料。同时,网盘还提供了搜索功能,方便学生快速定位和获取所需资料。 acwing算法基础课网盘的使用对于学生们的学习非常有帮助。通过下载和学习这些资源,学生们可以更好地理解课程内容,加深对算法的理解。此外,学生们还可以通过研究代码模板,学习优秀的编程思想和技巧,提高自己的编程能力。 总之,acwing算法基础课网盘是一项非常便利和实用的服务,为学生们提供了更加全面和深入的学习资源,帮助他们更好地掌握和运用算法知识。 ### 回答2: acwing算法基础课是一门优质的算法学习资源,其中的课程内容丰富多样,涵盖了算法基础知识、数据结构、动态规划、图论等等。很多学习者都认为这门课程对他们的算法学习有很大的帮助。 网盘是指以网络为媒介,提供文件存储和下载服务的云存储平台。acwing算法基础课也提供了网盘服务,方便学习者下载课程资料并进行学习。 通过acwing算法基础课网盘,学习者可以方便地获取到课程的各种学习资料,包括讲义、习题集、代码示例等。这些资料可以帮助学习者更好地理解和掌握课程的内容。此外,网盘还提供了上传和分享功能,学习者可以将自己的学习心得、代码等资料分享给其他学习者,促进学习者之间的互相学习和交流。 acwing算法基础课网盘的优点不仅仅是方便快捷的下载和分享功能,还包括安全可靠的存储环境。学习者可以放心地将自己的学习资料上传到网盘进行备份,减少数据丢失的风险。同时,网盘还提供了多种存储空间容量的选择,满足学习者不同的需求。 总的来说,acwing算法基础课网盘为学习者提供了方便、安全和多样化的学习资源下载和分享服务,为学习者的算法学习和进步提供了有力的支持。如果你对算法感兴趣,我推荐你去尝试一下这门精彩的课程!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值