0
点赞
收藏
分享

微信扫一扫

[第334场周赛]dijikstra,二分

​​6369. 左右元素和的差值​​

难度简单4

给你一个下标从 0 开始的整数数组 ​​nums​​ ,请你找出一个下标从 0 开始的整数数组 ​​answer​​ ,其中:

  • ​answer.length == nums.length​
  • ​answer[i] = |leftSum[i] - rightSum[i]|​

其中:

  • ​leftSum[i]​​ 是数组 ​​nums​​ 中下标 ​​i​​ 左侧元素之和。如果不存在对应的元素,​​leftSum[i] = 0​​ 。
  • ​rightSum[i]​​ 是数组 ​​nums​​ 中下标 ​​i​​ 右侧元素之和。如果不存在对应的元素,​​rightSum[i] = 0​​ 。

返回数组 ​​answer​​ 。

 

示例 1:

输入:nums = [10,4,8,3]
输出:[15,1,11,22]
解释:数组 leftSum 为 [0,10,14,22] 且数组 rightSum 为 [15,11,3,0] 。
数组 answer 为 [|0 - 15|,|10 - 11|,|14 - 3|,|22 - 0|] = [15,1,11,22] 。

示例 2:

输入:nums = [1]
输出:[0]
解释:数组 leftSum 为 [0] 且数组 rightSum 为 [0] 。
数组 answer 为 [|0 - 0|] = [0] 。

 

提示:

  • ​1 <= nums.length <= 1000​
  • ​1 <= nums[i] <= 105
Solution

前缀和,后缀和。模拟即可。

class Solution:
def leftRigthDifference(self, nums: List[int]) -> List[int]:
n = len(nums)
pref = [0]*n
suf = [0]*n
for i in range(1, n):
pref[i] = pref[i-1] + nums[i-1]
tot = pref[n-1] + nums[n-1]
res = [0]*n
for i in range(0, n):
res[i] = abs(pref[i]-tot+nums[i]+pref[i])
return res

​​6368. 找出字符串的可整除数组​​

难度中等3

给你一个下标从 0 开始的字符串 ​​word​​ ,长度为 ​​n​​ ,由从 ​​0​​ 到 ​​9​​ 的数字组成。另给你一个正整数 ​​m​​ 。

​word​​ 的 可整除数组 ​​div​​  是一个长度为 ​​n​​ 的整数数组,并满足:

  • 如果 ​​word[0,...,i]​​ 所表示的 数值 能被 ​​m​​ 整除,​​div[i] = 1​
  • 否则,​​div[i] = 0​

返回 ​word​​ 的可整除数组。

 

示例 1:

输入:word = "998244353", m = 3
输出:[1,1,0,0,0,1,1,0,0]
解释:仅有 4 个前缀可以被 3 整除:"9"、"99"、"998244" 和 "9982443" 。

示例 2:

输入:word = "1010", m = 10
输出:[0,1,0,1]
解释:仅有 2 个前缀可以被 10 整除:"10" 和 "1010" 。

 

提示:

  • ​1 <= n <= 105
  • ​word.length == n​
  • ​word​​ 由数字 ​​0​​ 到 ​​9​​ 组成
  • ​1 <= m <= 109
Solution

这题坑在str转int存在最大上限。所以就不能这么做,而需要考虑同余。

class Solution:
def divisibilityArray(self, word: str, m: int) -> List[int]:
n = len(word)
res = [0]*n
rem = 0
for i in range(n):
rem = (rem*10+int(word[i]))%m
if rem == 0:
res[i] = 1
return res

​​6367. 求出最多标记下标​​

难度中等11

给你一个下标从 0 开始的整数数组 ​​nums​​ 。

一开始,所有下标都没有被标记。你可以执行以下操作任意次:

  • 选择两个 互不相同且未标记 的下标 ​​i​​ 和 ​​j​​ ,满足 ​​2 * nums[i] <= nums[j]​​ ,标记下标 ​​i​​ 和 ​​j​​ 。

请你执行上述操作任意次,返回 ​nums​​ 中最多可以标记的下标数目。

 

示例 1:

输入:nums = [3,5,2,4]
输出:2
解释:第一次操作中,选择 i = 2 和 j = 1 ,操作可以执行的原因是 2 * nums[2] <= nums[1] ,标记下标 2 和 1 。
没有其他更多可执行的操作,所以答案为 2 。

示例 2:

输入:nums = [9,2,5,4]
输出:4
解释:第一次操作中,选择 i = 3 和 j = 0 ,操作可以执行的原因是 2 * nums[3] <= nums[0] ,标记下标 3 和 0 。
第二次操作中,选择 i = 1 和 j = 2 ,操作可以执行的原因是 2 * nums[1] <= nums[2] ,标记下标 1 和 2 。
没有其他更多可执行的操作,所以答案为 4 。

示例 3:

输入:nums = [7,6,8]
输出:0
解释:没有任何可以执行的操作,所以答案为 0 。

 

提示:

  • ​1 <= nums.length <= 105
  • ​1 <= nums[i] <= 109
Solution

这题用贪心做错了,考察的点并不是贪心,而是二分。

需要想到一个结论,就是匹配的话一定是最小的一半数匹配最大的一半数,可以通过反证法证明这样的解是最优的。

那么有了这一点之后,我们sort一下,之后对于k进行二分,由最小的k个数去匹配最大的k个数,二分查找答案即可。

另一方面,也可以用双指针。枚举小半边的数,每次让小的数去匹配大半边的数,匹配成功则换下一个数继续匹配,匹配失败则继续匹配。可以证明这样做的正确性。

[第334场周赛]dijikstra,二分_leetcode周赛

而我的方法则比较奇葩,是枚举小半边的数每个数能够匹配多少个数。之后找个数,如果有k个数能够匹配至少1,2,3,……k个数,那么答案就能是k。寻找最大的k直到找不到即可。思路比较混乱所以没有什么参考价值。

class Solution {
public:
int maxNumOfMarkedIndices(vector<int>& nums) {
sort(nums.begin(), nums.end());
int n = nums.size();
vector<int> cd(n/2);
for(int i=0;i<n/2;i++){
int k = upper_bound(nums.begin(), nums.end(), nums[i], [](int val, auto& x){return 2*val <= x;}) - nums.begin();
if(k==n)continue;
cd[i] = n-k;
}
int res = 0;
sort(cd.begin(), cd.end());
for(int i=0;i<n/2;i++){
if(cd[i]<1)continue;
if(cd[i]>res){
res += 1;
}
}
return res*2;
}
};

​​6366. 在网格图中访问一个格子的最少时间​​

难度困难12

给你一个 ​​m x n​​ 的矩阵 ​​grid​​ ,每个元素都为 非负 整数,其中 ​​grid[row][col]​​ 表示可以访问格子 ​​(row, col)​​ 的 最早 时间。也就是说当你访问格子 ​​(row, col)​​ 时,最少已经经过的时间为 ​​grid[row][col]​​ 。

你从 最左上角 出发,出发时刻为 ​​0​​ ,你必须一直移动到上下左右相邻四个格子中的 任意 一个格子(即不能停留在格子上)。每次移动都需要花费 1 单位时间。

请你返回 最早 到达右下角格子的时间,如果你无法到达右下角的格子,请你返回 ​​-1​​ 。

 

示例 1:

[第334场周赛]dijikstra,二分_leetcode周赛_02

输入:grid = [[0,1,3,2],[5,1,2,5],[4,3,8,6]]
输出:7
解释:一条可行的路径为:
- 时刻 t = 0 ,我们在格子 (0,0) 。
- 时刻 t = 1 ,我们移动到格子 (0,1) ,可以移动的原因是 grid[0][1] <= 1 。
- 时刻 t = 2 ,我们移动到格子 (1,1) ,可以移动的原因是 grid[1][1] <= 2 。
- 时刻 t = 3 ,我们移动到格子 (1,2) ,可以移动的原因是 grid[1][2] <= 3 。
- 时刻 t = 4 ,我们移动到格子 (1,1) ,可以移动的原因是 grid[1][1] <= 4 。
- 时刻 t = 5 ,我们移动到格子 (1,2) ,可以移动的原因是 grid[1][2] <= 5 。
- 时刻 t = 6 ,我们移动到格子 (1,3) ,可以移动的原因是 grid[1][3] <= 6 。
- 时刻 t = 7 ,我们移动到格子 (2,3) ,可以移动的原因是 grid[2][3] <= 7 。
最终到达时刻为 7 。这是最早可以到达的时间。

示例 2:

[第334场周赛]dijikstra,二分_leetcode周赛_03

输入:grid = [[0,2,4],[3,2,1],[1,0,4]]
输出:-1
解释:没法从左上角按题目规定走到右下角。

 

提示:

  • ​m == grid.length​
  • ​n == grid[i].length​
  • ​2 <= m, n <= 1000​
  • ​4 <= m * n <= 105
  • ​0 <= grid[i][j] <= 105
  • ​grid[0][0] == 0​
Solution

这题都在说dijikstra,但是讲真我还真没看出和dijikstra哪里像了。

首先,由于不能停留,因此我在一个位置上的时间的奇偶性和格子位置的奇偶性是有相关性的,如果不满足奇偶性那么就必须+1。其次,只要我能走到(0,1)或者(1,0),那么就一定可以通过反复横跳的方式达到终点。所以这个就可以看成是一个最短路问题。不过这个还是很奇特……

这里的距离是时间,到达每个点都有一个最短的时间,并且一定可以由它的全部相邻节点的时间直接求出。

想清楚这个问题之后就不难理解了。先建立一个dis[][]数组统计最短时间,初始化为inf,之后创建优先队列遍历即可。那么为什么需要考虑往回走的情况呢?

考虑下面的情况,如果不考虑往回走的情况,那么会认为[2, 0]点的最短时间是10.

0   1

10 2

4   3

为什么要使用优先队列?

优先队列由一个点推导上下左右四个点的当前最短路。这样可以保证我每次发生最短路更新时,能够将这种更新进行传播,从而保证我每一次求出的都是最短路。而发生更新后,也会沿着这个更新继续进行下去。总之每次从优先队列中取出的都是当前能够达到的最短路,之后取出的一定比这个距离更长。这就是dijikstra的精髓。一旦我们取到某个点,那么它一定是最短路!

class Solution:
def minimumTime(self, grid: List[List[int]]) -> int:
m, n = len(grid), len(grid[0])
if grid[0][1] > 1 and grid[1][0] > 1:
return -1
for i in range(m):
for j in range(n):
# 必须同奇偶
if (grid[i][j]+i+j)%2 != 0:
grid[i][j] += 1
h = [(0, 0, 0)]
dis = [[inf] * n for _ in range(m)]
while True:
d, i, j = heapq.heappop(h)
if i == m - 1 and j == n - 1:
return d
for x, y in (i+1, j), (i-1, j), (i, j+1), (i, j-1):
if 0 <= x < m and 0 <= y < n:
nd = max(d + 1, grid[x][y])
if nd < dis[x][y]:
dis[x][y] = nd
heapq.heappush(h, (nd, x, y))

[第334场周赛]dijikstra,二分_leetcode周赛_04

灵神nb!

好了,去打会游戏继续写Matlab了。周末就这么愉(摆)快(烂)地过去了。

举报

相关推荐

0 条评论