798. 得分最高的最小轮调【困难题】【每日一题】
思路:【差分+前缀和】
这种困难题显然我是不会写的,所以我直接看题解,连题解都看了好久哎~。
看的是宫水三叶的题解。
遍历nums数组,设当前下标为i,设当前元素nums[i]经过k次轮调之后可以得分。
轮调即数组左移,所以经过k次轮调之后,i位置元素的新下标为i-k,当i-k小于0时,说明这个元素轮调到了原来位置的后面,此时新下标为(i-k+n)
mod
n。(设nums长度为n)。由于必须同时满足合法移动(即下标不能小于0),且能够得分,那么只能考虑范围更小的情况,于是假设新下标在i前边,即新下标为
i-k,那么新下标必然满足0<= i-k <= n-1,推导出 k<=i 且 k>=i+1-n
而同时,为了保证nums[i]经过k次轮调之后可以得分,那么k次轮调之后的新位置
i-k>=nums[i] ,推导出 k <= i-nums[i]
因为nums[i] >= 0, 所以 i-nums[i ] <= i,两个不等式取交集的话肯定取范围更小的那个,所以k的取值范围为
i+1-n <= k <= i-nums[i]
考虑这个k的取值区间仅当左端点<=右端点时有效,那么当左端点>右端点时,那么这个k的取值区间可以转换为 0<= k <= i-nums[i-1] 和 i+1-n <= k <= n-1 这两段。
确定好 k 的取值范围后,定义查分数组cnt,长度为n+1(多1位是防止数组越界),对左端点加1标记,右端点减1标记,最后累加求cnt数组的前缀和,即为每个轮调次数k能够取得的最大得分。
10.那个区间处理理解的还不是很透彻,也讲不太清楚,好菜呀~
代码:
class Solution {
public int bestRotation(int[] nums) {
int len = nums.length;
int[] cnt = new int[len+1];
for (int i = 0; i < len; i++) {
int l = (i+1) % len , r = (i-nums[i]+len) % len;
if (l<=r){
cnt[l] += 1;
cnt[r+1] -= 1;
}else {
cnt[0] += 1;
cnt[r+1] -= 1;
cnt[l] += 1;
cnt[len] -= 1;
}
}
int ans = 0,max = cnt[0];
for (int i = 1; i <= len ; i++) {
cnt[i] += cnt[i-1];
if (cnt[i] > max){
max = cnt[i];
ans = i;
}
}
return ans;
}
}
1109. 航班预订统计【中等题】
思路:【差分+前缀和】
- 遍历每条预订记录,定义记录第0位为起始航班编号start,第1位记录为结束航班编号end,第2位为每个航班上的预订座位数目num。
- 定义差分数组cnt,对每条记录的起始航班位置+num标记,对每条记录的结束航班位置-num标记。
- 对差分数组求前缀和。
代码:
class Solution {
public int[] corpFlightBookings(int[][] bookings, int n) {
int[] cnt = new int[n+1],ans = new int[n];
for (int[] booking : bookings) {
int start = booking[0]-1,end = booking[1]-1,num = booking[2];
cnt[start] += num;
cnt[end + 1] -= num;
}
ans[0] = cnt[0];
for (int i = 1; i < n; i++) {
ans[i] += ans[i-1] + cnt[i];
}
return ans;
}
}