Leetcode 3017. Count the Number of Houses at a Certain Distance II

  • Leetcode 3017. Count the Number of Houses at a Certain Distance II
    • 1. 解题思路
    • 2. 代码实现
  • 题目链接:3017. Count the Number of Houses at a Certain Distance II

1. 解题思路

这一题其实思路上还是比较简单的,显然任何一个图都可以拆分为以下三个部分:

  1. 环的左侧
  2. 环的右侧
A
B
C
D
E

因此,我们总可以将所有的城市关系分为下述三种:

  1. 环内部的两个城市
  2. 环外部的两个城市
  3. 环外部的一个城市与环内的一个城市

因此,我们只需要分类讨论上述三种情况下的各个distance的种数然后相加即可。

首先,对于环内的两个城市,这个是比较简单的,对于任何一个距离,从任何一个城市出发,从左从右都可以到达一个城市,因此,任何一个距离 d ≤ ⌊ n 2 ⌋ d \leq \lfloor \frac{n}{2} \rfloor d2n的距离都对应 2 n 2n 2n的走法,唯一需要注意一下的就是 n n n为奇数的情况,此时对于 ⌊ n 2 ⌋ \lfloor \frac{n}{2} \rfloor 2n的距离下,左右走到的都是同一个城市,因此需要修正为 n n n

然后,对于第二种情况,对于环外的两个城市,显然我们将环视为两个节点即可,此时就是一条单链,任意距离 d d d对应的走法总数就是 n − d n-d nd。不过,我们需要注意一下,因为环上的两个点事实上是不允许走到的,因此我们需要额外刨除一下以这两个点作为起点以及终点时的情况。

最后,对于第三种情况,则稍微复杂一点,显然,我们只需要考虑一侧进入到环的情况即可,此时我们不难按照1中的情况获得从环的入口到达环上任意一点的走法种数,然后,我们对环入口线段上的所有点进行一下累加即可,对应情况极为:

a b c d e a b c d e a b c d e a b c d e ⋯ \begin{aligned} a && b && c && d && e \\ && a && b && c && d && e \\ && && a && b && c && d && e \\ && && && a && b && c && d && e \\ && && && && && && && &\cdots \end{aligned} abacbadcbaedcbedcede

这个我们用一个累积数组进行计算即可。

当然,这里也有一个特殊情况,就是当环的起点和终点是同一个点时,此时走环反而要多走一步,因此我们直接需要过滤掉这种情况。

2. 代码实现

给出python代码实现如下:

class Solution:
    def countOfPairs(self, n: int, x: int, y: int) -> List[int]:
        x, y = (x, y) if x <= y else (y, x)
        if x == y:
            return [2*(n-1-i) for i in range(n)]

        inside = y-x+1
        left, right = x-1, n-y

        def cal_outer(a, b):
            m = a+b+2
            ans = [2*(m-1-i) for i in range(m)]
            for i in range(a):
                ans[i] -= 2
                ans[i+1] -= 2
            for i in range(b):
                ans[i] -= 2
                ans[i+1] -= 2
            ans[0] -= 2
            return ans + [0] * (n-len(ans))
        
        def cal_inner(m):
            if m % 2 == 0:
                ans = [m*2 for i in range(1, m//2)] + [m]
            else:
                ans = [m*2 for i in range(1, (m+1)//2)]
            return ans + [0] * (n-len(ans))
        
        def cal_cross(a, b):
            ans= [0 for _ in range(n)]
            if a == 0:
                return ans
            s = [1] + [2 for _ in range((b-1)//2)]
            if b % 2 == 0:
                s = s + [1]
            m = len(s)
            s = [0] + list(accumulate(s))
            for i in range(a+m-1):
                ans[i] = 2*(s[min(i+1, m)] - s[max(0, i-a+1)])
            return ans

        inner = cal_inner(inside)
        outer = cal_outer(left, right)
        left_cross = cal_cross(left, inside)
        right_cross = cal_cross(right, inside)
        ans = [inner[i]+outer[i]+left_cross[i]+right_cross[i] for i in range(n)]
        return ans

提交代码评测得到:耗时691ms,占用内存36.1MB。

你可能感兴趣的:(leetcode笔记,leetcode,3017,leetcode,hard,leetcode周赛381,图论,分类讨论)