算法——一维二维数组差分及前缀和

*一维差分

有一个n长度的歌单,某聚聚每轮听歌听到k处(含k)结束,现给出m个k,表示听了m轮,问每首歌听过几次——抽象自xdoj1276

思路

  1. 暴力模拟解决:
    利用循环,将每一轮的k及之前的数都加一,最后输出结果。——TLE
  2. 一维数组差分:
    记录每个k的位置,然后利用一维数组的差分来求解,避免了重复计数。

观察

1.假设n为4,m轮之后停在1,2,3,4首歌的次数为a,b,c,d次。
我们倒着来看,那么最后输出的是
第4首: d1=d
第3首: c1=c + d1
第2首: b1=b + c1
第1首: a1=a + b1

原理

不好表述.
但是可以这么想,第3首歌和第4首歌的差为c,第二首歌和第三首歌的差为b,以此类推,每首歌听到的次数都是下一首歌听到的次数加上这个点停止的次数。大概这个就是原理或者是差分名字的由来吧。

c语言实现

#include
#include
int main()
{
    int n, m;
    scanf("%d%d", &n, &m);
    int list[n];
    memset(list, 0, sizeof(list));
    while(m--)
    {
        int k;
        scanf("%d", &k);
        list[k-1]++;
    }
    for(int i = n-2; i >= 0 ; i--)
    {
        list[i] += list[i+1];
    }
    for(int i = 0; i < n; i++)
    {
        printf("%d\n", list[i]);
    }
    return 0;
}

样例输入

5 4
5
1
2
3

样例输出

4
3
2
1
1

*二维差分

有一个n*m的歌单,每一轮听歌,某聚聚选择一个节点(i, j),这个节点前的所有歌(u, v), (u <= i ,v <= j)都会听一次(其实就是左上角那个点和选择那个点组成对角线所在的矩形内的所有歌).给出每轮的节点,求H轮之后,每首歌听了几次。——抽象自xdoj1378

思路

  1. 暴力模拟——TLE
  2. 二维差分

观察

数字代表第几首歌
对应字母代表听了几次
1 2 3 ——a b c
4 5 6 ——d e f
7 8 9 ——g h i
第9首:i1=i
第8首:h1=h+i
第7首:g1=g+h1
第6首:f1=f+i1
第5首(这里要注意):e1 = e + f + h + i = e + f1 + h1 - i1
……以下省略

原理

同一维差分
但是由于右下角加了2次,所以要减掉一次。

注意!

由于差分时右下角的数重复叠加,计算时应该减去一遍.

c语言实现

#include
int a[1001][1001] = {0};
int main()
{
    int n, m, h;
    scanf("%d%d%d", &n, &m, &h);
    while(h--)//输入部分
    {
        int u, v;
        scanf("%d%d", &u, &v);
        a[u-1][v-1]++;
    }

    for(int i = n-1; i >= 0; i--)//算法部分
    {
        for(int j = n-1; j >= 0; j--)
        {
            a[i][j] += a[i+1][j] + a[i][j+1] - a[i+1][j+1];
        }
    }

    for(int i = 0; i < n; i++)//输出部分
    {
        for(int j = 0; j < m; j++)
        {
            printf("%d\n", a[i][j]);
        }
    }
    return 0;
}

样例输入

3 3 9
1 1
1 2
1 3
2 1
2 2
2 3
3 1
3 2
3 3

样例输出

9
6
3
6
4
2
3
2
1

结语

差分法能极大地减少运算量,十分巧妙地避免了TLE的出现.

扩展与思考

是否可以在一维二维数组中选定两个点,判断这两个点内每首歌听过的次数

你可能感兴趣的:(算法,c语言/c++)