一维差分基础

差分概念&性质

首先给出求法,对于数组2,5,4,3;它的差分数组是2,3,-1,-1;及dif[i] = nums[i] - nums[i - 1]; (i > 0)。性质,如果我们要对区间[l, r] 进行相同的修改操作(这里假设都增加1,减一则相反),我们只需要对dif(差分数组)的dif[l] + 1, dif[r + 1] - 1(这里假设l = 1, r = 3),即可,时间复杂度从O(n) -> O(1),差分数组变为3,3,-1,-2,对dif求前缀和得数组3,6,5,3;但是这样一趟流程下来(特别是求dif)时间复杂度为O(n),这里我们不求dif,我们假设dif的每一个元素为0,进行修改dif变为:1,0,0,-1,求得前缀和为1,1,1,0;即为区间修改量但是得不到增加后的数组,但是可以用来判断修改后的数组是否合法,比如判断1~3区间都能减少3,差分数组前缀和为3,3,3,0;很明显1区间不满足。

差分入门题1

https://leetcode.cn/problems/corporate-flight-bookings/
题目描述:
这里有 n 个航班,它们分别从 1 到 n 进行编号。
有一份航班预订表 bookings ,表中第 i 条预订记录 bookings[i] = [firsti, lasti, seatsi] 意味着在从 firsti 到 lasti (包含 firsti 和 lasti )的 每个航班 上预订了 seatsi 个座位。
请你返回一个长度为 n 的数组 answer,里面的元素是每个航班预定的座位总数。

示例 1:

输入:bookings = [[1,2,10],[2,3,20],[2,5,25]], n = 5
输出:[10,55,45,25,25]
解释:
航班编号 1 2 3 4 5
预订记录 1 : 10 10
预订记录 2 : 20 20
预订记录 3 : 25 25 25 25
总座位数: 10 55 45 25 25
因此,answer = [10,55,45,25,25]
示例 2:

输入:bookings = [[1,2,10],[2,2,15]], n = 2
输出:[10,25]
解释:
航班编号 1 2
预订记录 1 : 10 10
预订记录 2 : 15
总座位数: 10 25
因此,answer = [10,25]

思路

典型的区间修改问题,直接差分,最最巧的是原来没有预定记录,及差分数组就是全为0,所以直接在两端修改即可,但是关注LeetCode下标越界,和,从下标是0开始的问题,答案直接求前缀和即可。

代码

int* corpFlightBookings(int** bookings, int bookingsSize, int* bookingsColSize, int n, int* returnSize) {
	int *ret = (int *)malloc(sizeof(int)*n);
	memset(ret, 0, sizeof(int) * n);
	int i, j;
	for(int i = 0; i < bookingsSize; i++) {
		int letf = bookings[i][0];
		int right = bookings[i][1];
		int seats = bookings[i][2];
		ret[letf - 1] += seats;
		if(right < n) {
			ret[right] -= seats;
		}
	}
	for(int i = 1; i < n; i++) {
		ret[i] += ret[i - 1];
	}
	*returnSize = n;
	return ret;
}

差分入门题2

http://acm.hdu.edu.cn/showproblem.php?pid=1556
题目描述:
N个气球排成一排,从左到右依次编号为1,2,3…N.每次给定2个整数a b(a <= b),lele便为骑上他的“小飞鸽"牌电动车从气球a开始到气球b依次给每个气球涂一次颜色。但是N次以后lele已经忘记了第I个气球已经涂过几次颜色了,你能帮他算出每个气球被涂过几次颜色吗?

Input
每个测试实例第一行为一个整数N,(N <= 100000).接下来的N行,每行包括2个整数a b(1 <= a <= b <= N)。
当N = 0,输入结束。

Output
每个测试实例输出一行,包括N个整数,第I个数代表第I个气球总共被涂色的次数。

Sample Input
3
1 1
2 2
3 3
3
1 1
1 2
1 3
0

Sample Output
1 1 1
3 2 1

思路

和第一题一样依葫芦画瓢即可。

代码

#include<bits/stdc++.h>
using namespace std;

const int maxn = 100010;
int dif[maxn];

int main() {
	int l, r, k;
	while(~scanf("%d", &k))	{
		memset(dif, 0, sizeof(dif));
		for(int i = 1; i <= k; i++) {
			cin >> l >> r;
			dif[l] += 1, dif[r + 1] -= 1;
		}
		for(int i = 1; i <= k; i++) {
			dif[i] += dif[i - 1];
			if(i != k) {
				printf("%d ", dif[i]);
			}
			else {
				printf("%d\n", dif[i]);
			}
		}
	}
	return 0;
}

差分进阶题

http://vj.saikr.com/problem/%E5%9F%BA%E7%A1%80%E7%8F%AD%E7%BB%83%E4%B9%A0-1.4.7
题目描述:
大意:第一行先输入两个整数,n,m(分别表示天数,借的订单数),第二行输入n个整数(表示n天,每天可以用的教室数),后面m行输入 d,(借的数量),l,r(从l天借到r天)。问需要修改的订单编号,及在哪里教室的数量不够借。(具体请点击链接)

思路

在有了前两道题的基础上不难看出这道题用差分数组解决,但是这道题的数据范围是订单数和天数在1~10的6次方,难道要每一天的挨个计算吗?(这里我当时没想到我觉得是个好想法)假设第前k个订单能满足借的数量,那么需要修改的订单编号肯定在后面,如果前k个订单不能满足,那么需要修改的订单编号在1-k之间,这是不是像二分?,所以我们只需要求前k份需要的订单数(使用差分),这里与我写的概念和性质后半段联系了起来,及我们换一个思路,我们不需要去减去借去的教室(因为我一开始想的是根据原始的教室数量去计算差分数组,然后再左端减,右端加,但是我求了一次差分,时间复杂度为O(n),后面又要求前缀和,时间复杂度又加了O(n),然后在求前缀和的时候比较相对应的j教室数量是否会为负数),我们只需要用差分数组求出我们需要的数量我这里举个例子:假设教室数量为2 5 4 3; 在第一天和第三天借2个教室,初始化dif[10000000],我们是求需要的教室数量(然后比较需要的是否会大于原始的数量),及从减去变成加上,dif变为[2,0,0,-2],前缀和 -> [2,2,2,0],通过比较大小发现不会出现教室不足的情况。及对比我的解法少了O(n)。

代码

#include<stdio.h>
#include<math.h>
#include<stdlib.h>
#include<string.h>
/*
 * line : 原数列
 * l, r, d, : 题目要求的 对于每个区间的左端点 右端点 值
 * change : 差分数组
 * 题解:对采用的订单数进行二分,每次用差分数组优化处理当天需要的教室数并与当天可对外借出的教室数比较检验。-Megumin
 */

int line[1000010], l[1000010], r[1000010], d[1000010], change[1000010];
int n, m;

int check(int x) {
	memset(change,0,sizeof(change));
	for (int i = 1; i <= x; i++) {
		change[ l[i] ] += d[i];
		change[ r[i] + 1] -= d[i];
		//obj 1用差分数组对前x个操作进行处理
	}
	for (int i = 1; i <= n; i++) {
		change[i] += change[i - 1];
	}
	//obj 2抹平差分数组
	for (int i = 1; i <= n; i++)
		if(change[i] > line[i]) return false;
	//obj 3什么情况下是发生了问题?
	return true;
}

int main() {
	scanf("%d %d", &n, &m);
	for (int i = 1; i <= n; i++)
		scanf("%d", &line[i]);
	for (int i = 1; i <= m; i++)
		scanf("%d %d %d", &d[i], &l[i], &r[i]);
	if(check(m)) { //obj 4什么情况是全都可以成立
		printf("0");
		return 0;
	}
	int left = 1, right = m, mid;
	while(left<=right) {
		mid = (left + right) >> 1;
		if(check(mid))//obj 5
			left = mid + 1;
		else
			right = mid - 1;
	}
	printf("-1\n");
	printf("%d", left);
	return 0;
}

这里有题目链接提供的思路和大量模板,总的来说这是一道程序填空题。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WhereIsCodeFrom

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值