力扣第381场单周赛:3017.按距离统计房屋对数目

原题链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

问题描述:

给你三个 正整数 n 、x 和 y 。

在城市中,存在编号从 1 到 n 的房屋,由 n 条街道相连。对所有 1 <= i < n ,都存在一条街道连接编号为 i 的房屋与编号为 i + 1 的房屋。另存在一条街道连接编号为 x 的房屋与编号为 y 的房屋。

对于每个 k1 <= k <= n),你需要找出所有满足要求的 房屋对 [house1, house2] ,即从 house1 到 house2 需要经过的 最少 街道数为 k 。

返回一个下标从 1 开始且长度为 n 的数组 result ,其中 result[k] 表示所有满足要求的房屋对的数量,即从一个房屋到另一个房屋需要经过的 最少 街道数为 k 。

注意x 与 y 可以 相等 

提示:

  • 2 <= n <= 105
  • 1 <= x, y <= n

示例 1:

输入:n = 3, x = 1, y = 3
输出:[6,0,0]
解释:让我们检视每个房屋对
- 对于房屋对 (1, 2),可以直接从房屋 1 到房屋 2。
- 对于房屋对 (2, 1),可以直接从房屋 2 到房屋 1。
- 对于房屋对 (1, 3),可以直接从房屋 1 到房屋 3。
- 对于房屋对 (3, 1),可以直接从房屋 3 到房屋 1。
- 对于房屋对 (2, 3),可以直接从房屋 2 到房屋 3。
- 对于房屋对 (3, 2),可以直接从房屋 3 到房屋 2。

示例 2:

输入:n = 5, x = 2, y = 4
输出:[10,8,2,0,0]
解释:对于每个距离 k ,满足要求的房屋对如下:
- 对于 k == 1,满足要求的房屋对有 (1, 2), (2, 1), (2, 3), (3, 2), (2, 4), (4, 2), (3, 4), (4, 3), (4, 5), 以及 (5, 4)。
- 对于 k == 2,满足要求的房屋对有 (1, 3), (3, 1), (1, 4), (4, 1), (2, 5), (5, 2), (3, 5), 以及 (5, 3)。
- 对于 k == 3,满足要求的房屋对有 (1, 5),以及 (5, 1) 。
- 对于 k == 4 和 k == 5,不存在满足要求的房屋对。

示例 3:

输入:n = 4, x = 1, y = 1
输出:[6,4,2,0]
解释:对于每个距离 k ,满足要求的房屋对如下:
- 对于 k == 1,满足要求的房屋对有 (1, 2), (2, 1), (2, 3), (3, 2), (3, 4), 以及 (4, 3)。
- 对于 k == 2,满足要求的房屋对有 (1, 3), (3, 1), (2, 4), 以及 (4, 2)。
- 对于 k == 3,满足要求的房屋对有 (1, 4), 以及 (4, 1)。
- 对于 k == 4,不存在满足要求的房屋对。

解题代码:

class Solution {
    public long[] countOfPairs(int n, int x, int y) {
        //结构类似于手链,一个环加两条小链
        //手链示意图:
        //          环中心
        //
        //  环左             环右
        //
        //     环入口    环出口
        //
        //  左链             右链
        //设环长为m
        int m = Math.abs(y-x)+1;
        //设左链长为p
        int p = Math.min(x,y)-1;
        //设右链长为q
        int q = n - Math.max(x,y);
        long[] ans = new long[n];
        
        //左链右链合并统计,包括环入口和出口
        //环内时统计时须减去这部分
        int dis = x==y?0:1;
        for(int i=1;i<=p+q+dis;i++)
            ans[i-1]+=2*(p+q+dis-i+1);
        //环长小于等于2时没有考虑环的意义
        //即|x-y|<= 1
        if(m>2){
            //环内距离
            for(int i=1;i<=m/2;i++)
                ans[i-1]+=2*m;
            //环长为偶数的特殊情况
            if(m%2==0) ans[m/2-1]-=m;
            //减去环入口到环出口
            //因环内统计与左链右链合并统计时
            //环入口到环出口一共统计了两次
            ans[0]-=2;

            //左链至环入口左侧,距离至少为2
            //因左链到环入口
            //在左链右链合并统计时已统计
            //所以,左链至少要提供一点距离
            //环至少要提供一点距离
            if(p>0)      gc(ans,p,m/2,1,1);
            //左链至环入口右侧,距离至少为3
            //因左链到环入口或出口
            //在左链右链合并统计时已统计
            //所以,左链至少要提供一点距离
            //环至少要提供两点距离
            //环长为偶数时减少一次统计
            if(p>0&&m>4) gc(ans,p,(m-1)/2,1,2);
            //右链至环出口右侧,距离至少为2,理由同上
            if(q>0)      gc(ans,q,m/2,1,1);
            //右链至环出口左侧,距离至少为3,理由同上
            if(q>0&&m>4) gc(ans,q,(m-1)/2,1,2);
        }
        return ans;
    }
    //l:链长
    //r:半环长
    //sl:链至少提供距离数
    //sr:环至少提供距离数
    public void gc(long[] ans,int l,int r,int sl,int sr){
        int tmp = Math.min(r-sr,l-sl);
        for(int i=sl+sr;i<=l+r;i++){
            int z = Math.min(l+r-i,i-sr-sl);
            z = Math.min(tmp,z)+1;
            ans[i-1]+=2*z;
        }
    }
}

解题思路应该和大部分人都差不太多,只不过,我是换了一种方式,一种便于我自己理解的方式。

以链长(p,q)与环长(m)代替对参数(n,x,y)的讨论,实际上,可以在使用时直接引用的,因链长与环长本质上还是来自于(n,x,y)。

对题目的讨论分为了六类:

一.左链连接到右链的一整条链,也是环长m<=2(即|x-y|<=1)的通用解

二.对环内的距离的讨论,对部分情况进行了修正,减去了重复计算的部分

三.链到环内距离的讨论,又分为了四种

        1.左链到环入口左侧,链至少需要提供一点距离,环至少也得提供一点距离

        2.左链到环出口右侧,链至少需要提供一点距离,环至少提供两点距离

        3.右链到环出口右侧,链至少需要提供一点距离,环至少也得提供一点距离

        4.左链到环入口左侧,链至少需要提供一点距离,环至少提供两点距离

        注意,环长为偶数时,环内距离入/出口为m/2的点为同一个,所以用(m-1)/2进行区别统计,且不会影响环长为奇数的情况。实际上,m/2与(m-1)/2不论放在哪一步进行统计都是合理的,但需要对统计前提条件进行调整。

增加统计前提条件,仅是为了防止程序进行大量无效统计。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值