蓝桥备赛(25)算法篇【差分】
一、差分
前缀和和差分的核心思想是预处理 , 可以在暴力枚举的过程中 , 快速给出查询结果 , 从而优化时间复杂度 。
最经典的用空间替换时间的做法。
学完差分之后 , 大家会发现 , 前缀和与差分是一对互逆的运算
二、一维差分
登录—专业IT笔试面试备考平台_牛客网
解法一:暴力解法 --> 直接模拟
两层 for 循环 --> O(m*n) = 10^10 (超时了...)
所以我们可以总结出 用差分解决问题的模板:
1) 创建差分数组
2)利用差分数组处理 m 次 修改操作
3)还原成原始数组:
#include <iostream>
using namespace std;
typedef long long LL;
const int N = 1e5 + 10;
LL n,m;
LL a[N];
LL f[N];//记录差分数组
int main()
{
cin >> n >> m;
//利用差分数组定义创建差分数组
for(int i = 1;i<=n;i++)
{
cin >> a[i];
f[i] = a[i] - a[i-1];
}
//处理 m 次修改操作
while(m--)
{
LL l,r,k;
cin >> l >> r >> k;
f[l] += k;
f[r+1] -= k;
}
//还原出原始的数组
for(int i = 1;i<=n;i++)
{
a[i] = a[i-1] + f[i];
cout << a[i] << " ";
}
return 0;
}
三、海底高铁
P3406 海底高铁 - 洛谷
#include <iostream>
using namespace std;
typedef long long LL;
const int N = 1e5 + 10;
int n,m;
LL f[N];//差分数组
int main()
{
cin >> n >> m;
//x->y
int x,y;
cin >> x;
//处理差分数组
for(int i = 2;i<=m;i++)
{
cin >> y;
//x->y
if(x > y)f[y]++,f[x]--;
else f[x]++,f[y]--;
x = y;
}
//利用差分数组,还原出原数组
for(int i = 1;i<=n;i++)
{
f[i] += f[i-1];
}
//直接求结果
LL ret = 0;
for(int i = 1;i<=n;i++)
{
LL a,b,c;
cin >> a >> b >> c;
ret += min(a*f[i],c+b*f[i]);
}
cout << ret << endl;
return 0;
}
四、二维差分
登录—专业IT笔试面试备考平台_牛客网
第一个能想到的就是暴力枚举 , 到了目标区域 , 直接进行操作,
但是时间复杂度太高了 , OJ平台是不会给过的~
借助二维差分数组 :
画图 ! 了解二维差分数组的性质 , 等再次使用的时候 , 上手会很快!!!
#include <iostream>
using namespace std;
typedef long long LL;
const int N = 1001;
LL n,m,q;
LL f[N][N];//差分矩阵
//差分矩阵的性质
void insert(int x1,int y1,int x2, int y2,int k)
{
f[x1][y1] += k;
f[x1][y2+1] -= k;
f[x2+1][y1] -=k;
f[x2+1][y2+1] +=k;
}
int main()
{
cin >> n >> m >> q;
//预处理--构造差分矩阵
for(int i = 1;i<=n;i++)
{
for(int j = 1;j<=m;j++)
{
LL x;cin >> x;
//[i,j] 为左上角,[i,j]为右下角的矩阵,统一加上x
insert(i,j,i,j,x);
}
}
//处理q次修改操作
while(q--)
{
int x1,y1,x2,y2,k;
cin >> x1 >> y1 >> x2 >> y2 >> k;
insert(x1,y1,x2,y2,k);
}
//利用前缀和还原修改之后的数组
for(int i = 1;i<=n;i++)
{
for(int j = 1;j<=m;j++)
{
f[i][j] = f[i-1][j] + f[i][j-1] - f[i-1][j-1] + f[i][j];
cout << f[i][j] << " ";
}
cout << endl;
}
return 0;
}
五、地毯
P3397 地毯 - 洛谷
差分数组走起来 , 简简单单~
#include <iostream>
using namespace std;
const int N = 1010;
int f[N][N];
int n,m;
void insert(int x1,int y1,int x2,int y2)
{
f[x1][y1]++;
f[x2+1][y1]--;
f[x1][y2+1]--;
f[x2+1][y2+1]++;
}
int main()
{
cin >> n >> m;
while(m--)
{
int x1,y1,x2,y2;
cin >> x1 >> y1 >> x2 >> y2;
insert(x1,y1,x2,y2);
}
//借助前缀和还原成原数组
for(int i = 1;i<=n;i++)
{
for(int j = 1;j<=n;j++)
{
f[i][j] = f[i-1][j] + f[i][j-1] - f[i-1][j-1] + f[i][j];
cout << f[i][j] << " ";
}
cout << endl;
}
return 0;
}
在理解的基础上可以背背 , 但是尽量不要背 , 理解为主