今日复习内容:还是做题
例题1:四元组问题
问题描述:
从小学开始,小明就是一个非常喜欢数学的孩子。他喜欢用数学的方式解决各种问题。在他的高中时期,他遇到了一个非常有趣的问题,那就算给定一个长度为n的整数数组nums,判断是否存在四个不同的下标a,b,c,d,使得a < b < c < d,并且nums[d] < nums[c] < nums[a] < nums[b]。
小明非常喜欢这个问题,他决定用数学的方式来解决它。他首先想到了一个非常简单的方法,那就是暴力枚举。他用四个循环来枚举所有可能的下标组合,然后判断是否满足条件。但是这个方法非常耗时,当n很大时,计算量会非常大。
所以请求你给出一个快速智慧的方法。
输入格式:
输入仅两行,第一行包含一个整数n,第二行包含n个整数,其含义如上所述。
输出格式:
输出仅一行,包含一个字符串,YES表示题目存在上述描述的情况,否则输出NO。
参考答案:
n = int(input())
li = list(map(int,input().split()))
a = -math.inf
stack = []
min_r = [math.inf] * n
for i in range(n - 2,-1,-1):min_r[i] = min(min_r[i + 1],li[i + 1])
flag = False
for i in range(n):if li[i] < a and li[i] > min_r[i]:print('YES')flag = Truebreakwhile stack and li[i] > stack[-1]:a = max(a,stack[-1])stack.pop()stack.append(li[i])
if not flag:print('NO')
运行结果:
以下是我对此题的理解:
这个问题要求判断是否存在四个不同的下标a,b,c,d,满足条件a < b < c < d,并且对应的数组元素满足nums[d] < nums[c] < nums[a] < nums[b]。换句话说,要找到数组中的四个元素,它们的大小顺序满足给定的条件。
对于解决这个问题的思路,我们可以尝试优化暴力枚举的方法,通过观察可以发现,我们需要维护一些关键的信息来进行判断。具体思路如下:
1维护一个递减的栈,栈中存放的是数组元素,用于寻找满足条件的a,b,c,d;
2.维护一个变量a,表示在当前位置之前的最大值,用于判断nums[a] < nums[b]的条件。
3.维护一个数组min_r,记录从当前位置开始到数组末尾的最小值,用于判断nums[d] < nums[c]的条件
n = int(input()):读取整数n,表示数组的长度
li = list(map(int,input().split())):读取一行整数,表示数组nums
a = -math.inf:初始化变量a,表示当前位置之前的最大值,初始值为负无穷
在这段代码中,变量a用来表示当前位置之前的最大值。初始将其设置为负无穷是为了确保在数组a中的初始值比任何数组元素都要小,这样可以保证在后续的变量中,a的值能够被正确更新为当前位置之前的最大值。这种设置可以保证a在后续的比较中能够正确地表示当前位置之前的最大值,保证算法的正确性。
stack = []:初始化一个空栈,用于维护递减序列
min_r = [math.inf] * n:初始化数组min_r,用于记录从当前位置开始到数组末尾的最小值,初始值为正无穷
在这段代码中,数组min_r被用来记录从当前位置开始到数组末尾的最小值。初始将其设置为正无穷是为了确保在后续的遍历中,min_r的初始值比任何数组元素都要大。这样可以保证在后续的遍历中,min_r能够正确更新为当前位置之后的最小值,这种设置可以保证min_r在后续的比较中能够正确地表示当前位置之后的最小值,保证算法的正确性。
for i in range(n - 2,-1,-1):遍历的起始位置是n-2,即倒数第二个位置,因为在更新min_r[i]时,需要用到li[i + 1]和min_r[i + 1],即当前位置的下一个位置的值,所有遍历的位置选择倒数第二个位置,是为了确保在更新min_r数组时,能够正确的利用到每个位置后面的元素信息。遍历的结束位置是-1,这是因为要遍历到数组的第一个位置,步长为-1,从后往前。以这种方式进行遍历可以确保在更新min_r时,每个位置的值都是其后所有元素的最小值,保证min_r的正确性。
flag = False:初始化标志位flag,表示是否找到满足条件的4个下标
for i in range(n):遍历数组元素
if li[i] < a and li[i] > min_r[i]:判断是否存在满足条件的4个下标
如果满足条件,则输出‘YES’,设置flag为True,并跳出循环
while stack and li[i] > stack[-1]:当栈不为空且当前元素大于栈顶元素时,执行以下操作:
a = max(a,stack[-1]):更新a为当前元素和栈顶元素的最大值
stack.pop():弹出栈顶元素
stack.append(li[i]):将当前元素压入栈中
if not flag:print('NO'):如果没有找到满足条件的四个下标,则输出NO
这段代码通过维护一个递减的栈和一个记录最小值的数组,以及一个变量记录最大值,来判断是否存在满足条件的4个下标,从而判断是否存在满足条件的4个元素。
标志位flag被用来表示是否找到满足条件的4个下标,初始将其设置为False,是为了在遍历整个数组后,如果没有找到满足条件的4个下标,就能够根据flag的值来判断并输出结果。如果在遍历过程中找到了满足条件的4个下标,则会将flag设置为True,而如果遍历完成后flag仍然为False,就直接输出‘NO’。因此将flag设置为False,是为了在后续的遍历中能够正确地判断是否找到了满足条件的4个下标,从而输出相应的结果。
例题2:肖恩的投球游戏
问题描述:
小羊肖恩最近迷上了投球游戏,具体来说,在他面前摆放了n个球筐,第i个框开始有ai个球。
接下来小羊会进行q次操作,每次操作会给出3个整数l,r,c,会将第l个框到第r个框,都投入c个球,请你输出操作完成之后每个框各有多少个球。
输入格式:
第一行输入两个整数n和q,表示球筐个数和操作次数
第二行输入n个整数,表示每个球筐最开始的球数
接下来q行,每行输入3个整数l,r,c
数据范围保证:1 <= n,q <= 10^5,1 <= l <= r <= n,1 <= ai,c <= 10^5
输出格式:
输出一行n个整数,表示每个框最终的球的个数,以空格分开。
参考答案:
n,q = map(int,input().split())
li = list(map(int,input().split()))
# diff = [0] * n
# diff[0] = li[0]
# for i in range(1,n):
# diff[i] = li[i] - li[i - 1]
# print(*diff)
# 求差分数组
diff = [0] * (n + 1)
for i in range(n):diff[i] += li[i]diff[i + 1] -= li[i]
for i in range(q):l,r,c = map(int,input().split())l -= 1r -= 1diff[l] += cdiff[r + 1] -= c
# 求前缀和
s = [0]
for i in range(n):s.append(s[-1] + diff[i])
print(*s[1:])
运行结果:
以下是我对此题的理解:
1.输入部分
首先从输入中获取球筐的数量n和操作次数q
接着获取一个长度为n的列表li,表示每个球筐初始的球数。
2.构建差分数组
差分数组diff的长度为n + 1,是为了方便处理边界情况
遍历初始球数列表li,将每个球筐的球数加到对应的差分数组位置上,并将下一个位置减去同样的球数,以便后续计算前缀和。
3.处理操作
对于每次操作,从输入中获取操作的起始球筐索引1,结束球筐索引r和投入的球数c
由于题目中球筐的编号是从1开始的,但代码中使用了从0开始的索引,所以需要将l和r都减去1
对差分数组进行更新,将起始球筐位置l加上投入的球数c,结束球筐位置r + 1减去投入的球数c,这样,差分数组记录了每个球筐的变化量。
4.计算前缀和
计算一个长度为n + 1的前缀和数组s,用于存储差分数组的前缀和
遍历差分数组diff,依次累积求和,得到前缀和数组s
5.输出结果
输出前缀和数组s的第一个元素到最后一个元素,即为每个球筐最终的球数。
例题3:泡澡
问题描述:
在一个寒冷的冬天,有N个人想要去澡堂泡澡,第i个人会在时间段[Si,Ti](不包括Ti)内每分钟使用Pi升热水。由于该澡堂设备简陋,无法 存储热水。热水器在每分钟最多能提供W升热水,现在请问该澡堂该澡堂能否满足这N个人的泡澡要求,如果可以请输出‘YES’,否则输出'NO'。
输入格式:
第一行包含两个整数N和W,表示洗澡的人数和热水器的容量。
接下来N行,每行包含3个整数Si,Ti,Pi(0 <= Si < Ti <= 2 * 10^5 ,1 <= W ,Pi <= 10^9),表示第i个人的洗澡计划。其中Si和Ti表示计划的开始时间和结束时间,Pi表示每分钟需要的热水量。
输出格式:
如果可以按照所有人的计划供应热水,则输出'YES',否则输出‘NO’。
参考答案:
n,w = map(int,input().split())
N = 200010
a = [0] * N
diff = [0] * N
for i in range(n):s,t,p = map(int,input().split())diff[s] += pdiff[t] -= p
a[0] = diff[0]
for i in range(1,N):a[i] = a[i - 1] + diff[i]
if max(a) <= w:print('Yes')
else:print('No')
运行结果:
以下是我对此题的理解:
首先输入洗澡的人数n和热水器的容量w,并初始化一个足够大的数组N和两个长度为N的数组a和diff。
对于每个人的洗澡计划,从输入中获取开始时间s,结束时间t和每分钟需要的热水量p
在差分数组diff中,将开始时间s对应的位置加上每分钟需要的热水量p,将结束时间t对应的位置减去每分钟需要的热水量p。这样,差分数组记录了每个时间点热水的变化量。
然后通过差分数组diff计算出数组a,其中a[i]表示第i分钟澡堂内的总热水量,这里使用了差分数组的前缀和思想。
判断数组a中的最大值是否小于等于热水器容量w,如果是,则说明可以按照所有人的计划供应热水,输出'Yes',否则输出'No‘
这段代码利用了差分数组和前缀和的思想,以及对时间段内热水量的变化进行记录,从而得出判断结果。
例题4:肖恩的投球游戏加强版
问题描述:
小羊肖恩最近喜欢上了投球游戏,但他已经不满足只有一行球筐的玩法了。
具体来说,在他面前摆放了n * m个球筐,这些球筐形成了一个n * m的矩阵,整数ai,j表示第i行第j列的球筐最开始的球的个数。
接下来小羊会进行q次操作,每次操作会给出5个整数x1,x2,y1,y2,c,他将会以(x1,y1)为左上角,(x2,y2)为右下角的球筐矩阵都投入c个球。请你输出操作完成之后每个球筐各含有多少个球。
输入格式:
第一行输入3个整数n,m,q,表示球筐矩阵的大小和操作次数
接下来n行,每行包含m个整数,表示球筐矩阵
接下来q行,每次输入5个整数x1,x2,y1,y2,c。
数据范围保证:
1 <= q <= 10^5,1 <= n,m <= 10^3,1 <= x1 <= x2 <= n,1 <= y1 <= y2 <= m,1 <= ai,j, c <= 10^5。
输出格式:
输出n行,每行m个数,表示操作完毕后每个球筐里球的个数。
参考答案:
n,m,q = map(int,input().split())
diff = [[0 for i in range(m + 2)]for j in range(n + 2)]
def pro(x1,y1,x2,y2,c):diff[x1][y1] += cdiff[x1][y2 + 1] -= cdiff[x2 + 1][y1] -= cdiff[x2 + 1][y2 + 1] += c
for i in range(1,n + 1):tmp = [0] + list(map(int,input().split()))for j in range(1,m + 1):pro(i,j,i,j,tmp[j])
for i in range(q):x1,y1,x2,y2,c = map(int,input().split())pro(x1,y1,x2,y2,c)
for i in range(1,n + 1):for j in range(1,m + 1):diff[i][j] += diff[i - 1][j] + diff[i][j - 1] - diff[i - 1][j - 1]print(*diff[i][1:-1])
运行结果:
以下是我对此题的理解:
1.输入处理
从输入中获取球筐矩阵的大小n,m和操作次数q
初始化一个二维差分数组diff,大小为(n + 2)*(m + 2),并全部初始化为0
2.投球操作函数pro
pro函数用于处理每次投球操作,接受左上角坐标(x1,y1),右下角坐标(x2,y2)以及投入的球数c
在差分数组diff中更新四个角的值,使得这个矩形区域内的元素加上或 减去相应的投球数。这样,差分数组记录了每个球筐矩形区域内的球数变化。
3.初始化球筐矩阵
通过两层循环,依次读取每个球筐的初始球数,并调用pro函数更新差分数组,这样差分数组就记录了初始时每个球筐的球数。
4.处理投球操作
通过循环读取每次的投球操作,调用pro函数更新差分数组,这一步模拟了小羊进行投球操作的过程。
5.计算前缀和
通过两层循环,计算差分数组的前缀和,得到最终每个球筐的球数。
OK,我今天要做的题还有很多,但是有点费时间,所以我写到了下一篇。
那这篇就这样了,下一篇继续!