问题描述
小蓝和小桥正在玩一个游戏,他们有一个长度为 nn 的序列,其中既有偶数也有奇数,且偶数和奇数的数量相等。
小蓝有一些零花钱,他可以用这些钱来做一个特殊的操作:他在序列中选取一个位置,然后在这个位置上将序列分成两段,要求每一段中偶数和奇数的数量都相等。小蓝想要用他的零花钱尽可能多地进行这个操作,但每次操作都需要花费代价。具体而言,每次选取的位置可以看成是对序列进行切割,切割需要花费的代价为切割两端的元素的差的绝对值。小蓝想知道,在他的预算范围内,最多能进行多少次操作。
请你帮助小蓝计算最多可以进行的操作次数。
输入格式
第一行包含两个整数 nn 和 BB(2≤n≤100,1≤B≤1002≤n≤100,1≤B≤100),表示序列的长度和小蓝拥有的零花钱数。
第二行包含 nn 个整数 a1,a2,⋯,ana1,a2,⋯,an(1≤ai≤1001≤ai≤100),表示给定的序列(保证在这 nn 个元素中奇数的个数等于偶数的个数)。
输出格式
输出一个整数,表示在小蓝的预算范围内,能够进行的最多操作次数。
输入:
6 3
1 2 3 4 5 6
输出:
2
思路:
题目要求是将序列分割成若干份且每段中奇数和偶数的数量相同。每次分割的代价就是分割点两端元素的差的绝对值。目标是在预算的范围中进行尽可能多的分割。因此代码分三部分:①统计奇偶性:通过遍历序列,统计奇数和偶数的差值(sum),当差值为0时说明当前端奇偶数量相同,可以分割。②记录代价:将每个可能的分割点的代价记录下来(cnt数组)③贪心选择:将代价从小到大排序,尽可能选择代价小的分割点,直到预算用完。
动态规划和贪心的区别:
动态规划通常用于解决具有重叠子问题和最优子结构问题,通过保存中间结果来避免重复计算;贪心算法每一步都做出局部最优选择,希望最终结果也是全局最优。
t = 1
ans = []
#预算范围中的操作次数
for _ in range(t):
n,B = map(int,input().split())
a = list(map(int,input().split()))
sum = 0
#奇偶数的差值
cnt = []
#分割点的代价
for i in range(n):
if a[i] & 1:
#奇数的二进制表示最后一位都是1 用于快速判断是否为奇数
sum += 1
else:
sum -= 1
if sum == 0 and i + 1 < n:#要考虑不要越界
cnt.append(abs(a[i+1]-a[i]))
cnt.sort()
ans = 0
#将得到的分割点计算代价 在B的范围内
for c in cnt:
if B >= c:
B -= c
ans += 1
else:
break
print(ans)
在给的事例中奇数个数等于偶数个数所以存在分割点。遍历序列后,1(奇数):sum = 1;2(偶数):sum = 0且i+1<n,即i=1,下一个元素为3,以此类推.....最终1到6的最终i=5,已到序列结尾不做处理,得到的代价cnt = [1,1];在 `2` 和 `3` 之间分割,代价为 `1`,分割后的子序列为 `[1, 2]` 和 `[3, 4, 5, 6]`。在 `4` 和 `5` 之间分割,代价为 `1`,分割后的子序列为 `[3, 4]` 和 `[5, 6]`。最后在预算范围内选择分割点:初始化Answer= 0,B=3,遍历cnt列表[1,1]:第一个代价 `1`:B >= 1,执行操作,B -= 1,ans += 1,此时 B = 2,ans = 1。 第二个代价 `1`:B >= 1,执行操作,B -= 1,ans += 1,此时 B = 1,`ans = 2。 由于 `cnt` 列表已经遍历完毕,最终 ans = 2。