欢迎光临
我们一直在努力

差分算法(一维差分、二维差分)

一、差分的定义

  • 差分数组(专门用来解决 把某个区间内的数加一个统一的数)。
  • 差分数组的值为:当前元素与前一个元素的差。、
  • 差分是前缀和的逆运算。
  • 二、一维差分

    模版题

    题目描述(题目来源于牛客网)

    思路

    方法一:暴力求解(直接模拟)
  • 使用数组将初始输入数据存储起来。
  • 对于每次输入的l,r,k,对区间[l,r]中的数据进行for循环依次给每个数加k。
  • 时间复杂度:执行m次,每次循环执行r – l 次,r – l + 1最大为n。所以时间复杂度为O(nm)。
  • 代码展示

    #include<iostream>

    using namespace std;

    const int N = 1e5 + 10;
    long long a[N];
    long long n, m;

    int main(){
    cin>>n>>m;

    //输入数组
    for(int i = 1; i<= n; i++){
    cin>>a[i];
    }

    //对区间加k,执行m次
    while(m–){
    long long l, r, k;
    cin>>l>>r>>k;
    for(int i = l; i <= r; i++){
    a[i] += k;
    }
    }

    //输出全部执行后的数组
    for(int i = 1; i <= n; i++){
    cout<<a[i]<<" ";
    }

    return 0;
    }

    方法二:一维差分数组
  • 使用数组a[]将初始输入数据存储起来。
  • 利用查分数组。
  • 差分数组s[i] = a[i] – a[i – 1]。
  • 对于每次加k的操作。差分数组只需改变两个数——->s[l] += k, s[r – 1] -= k。
  • 图解
  • 还原数组a[]。
  • a[i] = a[ i – 1 ] + s[i]。
  • 输出a[i]。
  • 图解
  • 时间复杂度:O(m+n)。
  • 代码展示

    #include<iostream>

    using namespace std;

    const int N = 1e5 + 10;
    long long a[N], s[N];
    long long n, m;

    int main(){
    //读入整数n,m
    cin>>n>>m;

    //输入数组
    for(int i = 1; i <= n; i++){
    cin>>a[i];
    }

    //计算差分数组
    for(int i = 1; i <= n; i++){
    s[i] = a[i] – a[i – 1];
    }

    //处理每一次操作
    while(m–){
    long long l, r, k;
    cin>>l>>r>>k;

    //对于每一次操作之后更新差分数组
    s[l] += k;
    s[r + 1] -= k;
    }

    //输出处理完全部操作之后的数组
    for(int i = 1; i <= n; i++){
    a[i] = s[i] + a[i – 1];
    cout<< a[i] <<" ";
    }

    return 0;
    }

    例题:海底高铁

    题目描述(题目来源于洛谷)

    思路

  • 使用数组a[]存储输入的城市。
  • 初始化差分数组
  • 遍历每一个城市,保证相邻两个城市中较小城市位于左边。
  • 使用前缀和得到次数数组。
  • 对于买票方案有两种,只买单程票、买IC卡和每次扣费b。取两者中小者就是最后的结果。
  • 代码

    #include<iostream>

    using namespace std;

    const int N = 1e5 + 10;
    long long a[N], s[N];
    long long n, m;

    int main(){
    cin>>n>>m;

    //读入城市
    for(int i = 1; i <= m; i++){
    cin>>a[i];
    }

    //计算差分数组
    for(int i = 1; i < m; i++){
    //遍历每一个城市—l表示起点,r表示终点,对区间[l,r]之间数加1
    if(a[i] < a[i + 1]){
    long long l = a[i];
    long long r = a[i + 1];
    s[l] += 1;
    s[r] -= 1;
    }else{
    long long l = a[i + 1];
    long long r = a[i];
    s[l] += 1;
    s[r] -= 1;
    }
    }

    // 将差分数组转换为每段铁路的实际次数(前缀和)
    long long sum = 0;
    for(int i = 1; i < n; i++){ // 只处理前 n-1 段铁路
    sum += s[i];
    s[i] = sum;
    }

    long long res = 0;
    //计算最小花费
    for(int i = 1; i < n; i++){
    long long a,b,c;
    cin>>a>>b>>c;
    res += min(a * s[i], c + b * s[i]);
    }
    cout<<res<<endl;
    return 0;
    }

    三、二维差分

    模版题

    题目描述(题目来源于牛客网)

    思路

  •  使用数组a[]存储输入矩阵。
  • 初始化差分数组s[]。
  • s[i][j] = a[i][j] – a[i-1][j] – a[i][j-1] + a[i-1][j-1]。
  • 注意考虑行列两个方向。
  • 对于每次加k操作,更新差分数组。
  • s[x1][y1] += k;
  • s[x1][y2+1] -= k;
  • s[x2+1][y1] -= k;
  • s[x2+1][y2+1] += k;
  • 利用差分数组还原原数组a[]。
  • a[i][j] = a[i-1][j] + a[i][j-1] – a[i-1][j-1] + s[i][j]。
  • 输出操作完成后的数组a[]。
  • 时间复杂度为:O(mn + q)
  • 代码

    #include<iostream>

    using namespace std;

    const int N = 1e3 + 10;
    long long a[N][N], s[N][N];
    long long n, m, q;

    int main(){
    cin>>n>>m>>q;

    //输入矩阵
    for(int i = 1; i <= n; i++){
    for(int j = 1; j <= m; j++){
    cin>>a[i][j];
    }
    }

    //初始化差分数组
    for(int i = 1; i <= n; i++){
    for(int j = 1; j <= m; j++){
    s[i][j] = a[i][j] – a[i – 1][j] – a[i][j – 1] + a[i – 1][j – 1];
    }
    }

    //对于每一次加k操作,更新差分数组
    while(q–){
    long long x1, y1, x2, y2, k;
    cin>> x1 >> y1 >> x2 >> y2 >> k;

    //更新差分数组
    s[x1][y1] += k;
    s[x1][y2 + 1] -= k;
    s[x2 + 1][y1] -= k;
    s[x2 + 1][y2 + 1] += k;

    }

    //还原a数组(计算前缀和)
    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] + s[i][j];
    }
    }

    //输出最后的结果
    for(int i = 1; i <= n; i++){
    for(int j = 1; j <= m; j++){
    cout<<a[i][j]<<" ";
    }
    cout<<endl;
    }
    return 0;
    }

    例题:地毯

    题目描述(题目来源于洛谷)

    思路

  • 注意这个题目不需要输入初始矩阵,初始矩阵全为0!!!
  • 初始化差分数组s[]。
  • s[i][j] = a[i][j] – a[i-1][j] – a[i][j-1] + a[i-1][j-1]。
  • 注意考虑行列两个方向。
  • 对于每次加k操作,更新差分数组。这里的k为1。
  • s[x1][y1] += k;
  • s[x1][y2+1] -= k;
  • s[x2+1][y1] -= k;
  • s[x2+1][y2+1] += k;
  • 利用差分数组还原原数组a[]。
  • a[i][j] = a[i-1][j] + a[i][j-1] – a[i-1][j-1] + s[i][j]。
  • 输出操作完成后的数组a[]。
  • 代码

    #include<iostream>

    using namespace std;

    const int N = 1e3 + 10;
    long long a[N][N] = {0}, s[N][N];
    long long n, m;

    int main(){
    cin>>n>>m;

    // //输入初始矩阵
    // for(int i = 1; i <= n; i++){
    // for(int j = 1; j <= n; j++){
    // cin>>a[i][j];
    // }
    // }

    //初始化差分数组
    for(int i = 1; i <= n; i++){
    for(int j = 1; j <= n; j++){
    s[i][j] = a[i][j] – a[i -1][j] – a[i][j – 1] + a[i -1][j – 1];
    }
    }

    //根据每次输入的地毯坐标,更新差分数组
    while(m–){
    long long x1, y1, x2, y2;
    cin>>x1>>y1>>x2>>y2;

    //更新差分数组
    s[x1][y1] += 1;
    s[x1][y2 + 1] -= 1;
    s[x2 + 1][y1] -= 1;
    s[x2 + 1][y2 + 1] += 1;
    }
    //根据差分数组还原数组a
    for(int i = 1; i <= n; i++){
    for(int j = 1; j <= n; j++){
    a[i][j] = a[i – 1][j] + a[i][j – 1] – a[i -1][j -1] + s[i][j];
    }
    }

    //输出矩阵
    for(int i = 1; i <= n; i++){
    for(int j = 1; j <= n; j++){
    cout<<a[i][j]<<" ";
    }
    cout<<endl;
    }
    return 0;
    }

    赞(0)
    未经允许不得转载:171主机测评 » 差分算法(一维差分、二维差分)
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址