
༺ 个人主页 · 纪念229 ༻
༒专栏目录:《算法》༒
༒专栏目录:《C++》༒
༒专栏目录:《MySQL数据库》༒
༒专栏目录:《前端开发》༒
༺世上本没有路,走的人多了自然就有了༻
本篇文章讲述的是C++ 基础算法里有关差分的一些题,希望对你有所帮助
题目链接:
1.一维差分


题目要求是将范围内的数据同时计算一个数打印出最终结果
这里我们可以用两层循环解也可以用双指针但最快的是用差分数组解
一维差分中的每个元素为fi = ai - ai - 1;(fi为差分中的元素ai是普通数组的元素)
用差分解题
写出差分数组
fi = ai - ai - 1;或者利用差分数组的性质 fi += x; fi + 1 -= x;
该意思其实与第一种是一样的好处是不用存储普通数组(第一种要调用两个普通元素必须要写好普通数组)
利用差分数组修改目标范围的数据
这里我们利用差分数组的性质来修改普通数组
如果我们要修改l到r的数据
直接fl +(或者其它运算符) = x; fr + 1 -(或者其它运算符) = x;就可以实现普通数组的l到r的数据同时发生改变
利用修改后的差分数组还原普通数组并且打印
将修改后的差分数组修改为普通数组
ai = fi + ai - 1; 我们如果用的是第二种方法写的差分数组就用这种 fi = fi - fi - 1第二种其实就是第一种因为我们从第一次还原开始fi - 1都实际上呢变成了ai - 1
差分数组也是从1开始(防止越界)
c
#include<iostream>
using namespace std;
typedef long long LL;
const int N = 1e5 + 10;
LL f[N];
int n, m;
void fix(int l, int r, int k)
{
f[l] += k, f[r + 1] -= k;
}
int main()
{
cin >> n >> m;
int x;
//预处理分差数组(利用分差数组的性质搞定分差数组)不用保留普通数组(f[i] = a[i] - a[i - 1])
for(int i = 1; i <= n; i++)
{
cin >> x;
f[i] += x;
f[i + 1] -= x;
}
int l, r, k;
//利用差分数组修改整形数组
for(int i = 1; i <= m; i++)
{
cin >> l >> r >> k;
fix(l, r ,k);
}
//将处理后的差分数组还原为计算后的结果数组
//最后打印修改完后的数组
for(int i = 1; i <= n; i++)
{
f[i] = f[i - 1] + f[i];//这里的f[i]我们把它看作a[i] = f[i] + a[i -1]从第一个开始f[1]就是a[1]然后递推f[i] = f[i -1] + f[i]实际上每次算完f[i]以前的f[b] 1<=b<i都变为了a[b] 所以f[i] = f[i -1] + f[i]就是a[i] = a[i - 1] + f[i];
cout << f[i] << " ";
}
return 0;
}
这里我们定义差分数组是利用差分数组的性质
2.海底高铁


题目的意思是输入一个N,M,N的意思是一共要经过N座城市(N - 1条线)
M的意思是一共要到M次站
要求我们求出最少钱的方式
因为这里每经过一条线都要收费所以我们从1到3或者从3到1就要收线路1和2的费用(这里我们用差分数组最方便)
每条路线有两种收费方式一种为i路线A费i路线经过的次数
一种是i路线B费 i路线经过的次数 + i路的C卡费
我们取最小的费用即可
最后将每条路线的最低费用加起来就是我们要的总费用
c
//海底高铁这道题就是看记录几号线坐了几次然后计算该线是直接买票便宜还是买卡在买票便宜我们去费用最低的
//这里因为这里可能跨多个线路所以我们用差分法计算方便
//分差数组下标从1开始防止越界
#include<iostream>
#include<algorithm>
typedef long long LL;
const LL K = 1e5 + 10;
LL f[K];
LL N, M;
using namespace std;
int main()
{
cin >> N >> M;
//直接根据差分数组运算不用预处理差分数组
//处理1与2,2与3的问题递推我们先在外面定义一个x循环从2开始里面输入y每次循环将y赋值给x即可
int x; cin >> x;
for(int i = 2; i <= M; i++)
{
int y; cin >> y;
if(x > y)//当x > y直接利用分差数组将下标为x减1y加1即因为从城市1到城市3所以只要用线路1和2所以只要减x 不用减 x + 1从而修改线路1,2,3
{
f[y] += 1;
f[x] -= 1;
}
else{
f[x] += 1;
f[y] -= 1;
}
x = y;//每一次循环将y赋值给x
}
//还原为普通数组
for(int i = 1; i < N; i++)//从1到N-1是因为N个城市N - 1个线路
{
f[i] = f[i - 1] + f[i];
}
//输入路线价格
long long a[N],b[N],c[N];
for(int i = 1; i < N; i++)
{
cin >> a[i] >> b[i] >> c[i];
}
//计算线路1到线路N - 1的最小价格
LL sum = 0;
for(int i = 1; i < N; i++)
{
//每条路线的乘车价格有两种取最少的
//i路线A价格*i路线到的次数
//以及i路线B价格*i路线到的次数加上该路线的乘车卡价格
LL tmp = min(a[i]*f[i], b[i]*f[i] + c[i]);//这里是加法tmp用long long
sum += tmp;
}
cout << sum;//最后打印最少乘车价格
return 0;
}
这题直接定义差分数组直接不用写差分数组直接修改差分数组即可
这里我们遍历1 于2以及 遍历 2到 3然后写差分数组是我们先将第一个数据x写在循环外再将第二个数据写在循环内(循环从2到M次(因为第一次在外面输入过了))y然后判断 要是x > y的话就 fy += 1 fx -= 1(为啥不是x + 1是因为只要修改到x - 1即可)否者 x < y
fx += 1; fy -= 1; 每次算完都将 x = y 这样就实现了 1到 3 3 到 2
c
for(int i = 2; i <= M; i++)
{
int y; cin >> y;
if(x > y)//当x > y直接利用分差数组将下标为x减1y加1即因为从城市1到城市3所以只要用线路1和2所以只要减x 不用减 x + 1从而修改线路1,2,3
{
f[y] += 1;
f[x] -= 1;
}
else{
f[x] += 1;
f[y] -= 1;
}
x = y;//每一次循环将y赋值给x
}
然后从1到N - 1遍历for循环有N个城市就有N - 1个线路)利用修改后的差分数组还原修改后的普通数组就是每条路线经过的次数
c
for(int i = 1; i < N; i++)//从1到N-1是因为N个城市N - 1个线路
{
f[i] = f[i - 1] + f[i];
}
//输入路线价格
long long a[N],b[N],c[N];
for(int i = 1; i < N; i++)
{
cin >> a[i] >> b[i] >> c[i];
}
然后遍历1到N - 1的线路(有N个城市就有N - 1个线路)
然后我们定义一个sum和tmp(因为涉及加法我们定义为long long类型我们直接typedef为LL把大多数定义的数据都用LL就懒得想了),tmp记录每条线路的最小费用(min)每次循环结束将tmp +=给 sum最后打印sum就是坐车的最小费用
c
//计算线路1到线路N - 1的最小价格
LL sum = 0;
for(int i = 1; i < N; i++)
{
//每条路线的乘车价格有两种取最少的
//i路线A价格*i路线到的次数
//以及i路线B价格*i路线到的次数加上该路线的乘车卡价格
LL tmp = min(a[i]*f[i], b[i]*f[i] + c[i]);//这里是加法tmp用long long
sum += tmp;
}
cout << sum;//最后打印最少乘车价格
return 0;
}
3.二维差分


这里的意思是给你个二维普通数组将某个矩阵的数据都发生改变
这里最优的方法就是用二维差分
二维差分的步骤
写二维差分
每个二维差分数组的元素fij += x ; fi + 1j -= x; fij + 1 -= x; fi + 1j + 1 += x;
也可以这样写利用二维差分的性质 fi\[j = aij - ai - 1j - aij - 1 + ai - 1j - 1其实就是 fij = x - fi - 1j - fij - 1 + fi - 1j - 1;
推荐第一种第一种我们用可以和二维差分数组的修改一起用
修改二维差分元素从而修改普通数组矩阵范围的数据
要修改x1,y1和x2,y2之间的数据
用fx1y1 += x; fx2 + 1y1 -= x; fx1y2 + 1 -= x; fx2 + 1y2 + 1 += x;
当x1 == x2 以及 y1 == y2的时候就是我们写二维差分数组的方式
将修改的二维差分数组的元素还原为修改后的普通二维数组顺便打印
还原不能使用二维分差数组的性质调用这个(x1y1 += x; fx2 + 1y1 -= x; fx1y2 + 1 -= x; fx2 + 1y2 + 1 += x;)是错误的
要用这个
aij = fij + fi - 1j + fij - 1 - fi - 1j - 1;其实就是fij = fij + fi - 1j + fij - 1 - fi - 1j - 1;因为fij之前的fxy都已经还原为普通二维数组的值顺便打印该值
c
//二维分差从下标1,1开始我们 每个分差元素为 f[i][j] = a[i][j] - a[i - 1][j] - a[i][j - 1] + a[i - 1][j - 1];
//我们也可以用 f[i][j] += k f[i + 1][j] -= k f[i][j + 1] -= k f[i + 1][j + 1]来实现分差数组(利用分差数组性质来初始化分差数组也可以利用用这个对分差数组进行修改从而修改普通数组的矩阵范围内的值)
#include<iostream>
using namespace std;
typedef long long LL;
const int N = 1010;
LL f[N][N];
int n, m ,q;
//利用二维差分的性质可以初始化差分数组可以改变差分从而改变规定范围内的二维普通数组
void insert(int x1, int y1, int x2, int y2, int x)
{
f[x1][y1] += x; f[x2 + 1][y1] -= x; f[x1][y2 + 1] -= x; f[x2 + 1][y2 + 1] += x;
}
int main()//调用main函数要用()
{
cin >> n >> m >> q;
//利用二维分差的性质初始化二维分差数组
LL x;
//分差数组从1开始防止越界
//初始化二维差分数组
for(int i = 1; i <= n; i++)
{
for(int j = 1 ; j <= m; j++)
{
cin >> x;
insert(i,j,i,j,x);
}
}
//利用二维差分修改普通矩阵的值
LL x1, y1, x2, y2, k;//在这里改为longlongk值可能超过int
for(int i = 1; i <= q; i++)//修改下标到q不是m
{
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][j] + f[i - 1][j] + f[i][j - 1] - f[i - 1][j - 1];//在f[i][j]后面的都已经还原为普通数组所以f[i][j]结果就是已经还原后的普通整形数组的元素
}
//最后打印还原后的普通数组
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= m; j++)
{
cout << f[i][j] << " ";
}
cout << endl;
}
return 0;
}
4.地毯


题目就是给一个n * n的二维数组我们在这个范围内放上m个毯子
每个毯子由x1,y1 x2, y2表示
求每个坐标被放了几个毯子
这里我们可以用二维差分数组
这里二维差分数组我们不用写直接定义然后根据毯子修改普通矩阵
然后将修改后的二维差分数组还原为修改后的二维普通数组并且打印
c
//每个坐标有几个毯子我们可以用分差数组来计算
#include<iostream>
using namespace std;
typedef long long LL;
//用分差数组解决从下标1开始
const int N = 1010;
LL f[N][N];
LL n, m;
int main()
{
cin >> n >> m;
//输入毯子修改差分数组(不用初始化开始都为0)
LL x1,y1,x2,y2 ;
for(int i = 1; i <= m; i++)
{
cin >> x1 >> y1 >> x2 >> y2;
f[x1][y1] += 1; f[x2 + 1][y1] -= 1; f[x1][y2 + 1] -= 1; f[x2 + 1][y2 + 1] += 1;//最后不是f[x1 + 1][y1 + 1] += 1是f[x2 + 1][y2 + 1] += 1
}
//还原差分数组为整形数组打印
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= n; j++)
{
f[i][j] = f[i][j] + f[i - 1][j] + f[i][j - 1] - f[i - 1][j - 1];
cout << f[i][j] << " ";
}
cout << endl;
}
return 0;
}
文章到此就告一段落,希望对你有所帮助,感谢观看!