LeetCode 1001. 网格照明 / 2006. 差的绝对值为 K 的数对数目 / 1447. 最简分数

这篇博客探讨了两个编程问题:1. 在一个网格上进行照明控制,根据给定的灯的位置和查询,计算并更新照亮的单元格;2. 统计数组中数对的数量,其差的绝对值等于给定值k。解决方案涉及到了哈希映射、矩阵操作和辗转相除法。
摘要由CSDN通过智能技术生成

1001. 网格照明

2022.2.08 每日一题

题目描述

在大小为 n x n 的网格 grid 上,每个单元格都有一盏灯,最初灯都处于 关闭 状态。

给你一个由灯的位置组成的二维数组 lamps ,其中 lamps[i] = [rowi, coli] 表示 打开 位于 grid[rowi][coli] 的灯。即便同一盏灯可能在 lamps 中多次列出,不会影响这盏灯处于 打开 状态。

当一盏灯处于打开状态,它将会照亮 自身所在单元格 以及同一 行 、同一 列 和两条 对角线 上的 所有其他单元格 。

另给你一个二维数组 queries ,其中 queries[j] = [rowj, colj] 。对于第 j 个查询,如果单元格 [rowj, colj] 是被照亮的,则查询结果为 1 ,否则为 0 。在第 j 次查询之后 [按照查询的顺序] ,关闭 位于单元格 grid[rowj][colj] 上及相邻 8 个方向上(与单元格 grid[rowi][coli] 共享角或边)的任何灯。

返回一个整数数组 ans 作为答案, ans[j] 应等于第 j 次查询 queries[j] 的结果,1 表示照亮,0 表示未照亮。

示例 1:

在这里插入图片描述
输入:n = 5, lamps = [[0,0],[4,4]], queries = [[1,1],[1,0]]
输出:[1,0]
解释:最初所有灯都是关闭的。在执行查询之前,打开位于 [0, 0] 和 [4, 4] 的灯。第 0 次查询检查 grid[1][1] 是否被照亮(蓝色方框)。该单元格被照亮,所以 ans[0] = 1 。然后,关闭红色方框中的所有灯。
在这里插入图片描述
第 1 次查询检查 grid[1][0] 是否被照亮(蓝色方框)。该单元格没有被照亮,所以 ans[1] = 0 。然后,关闭红色矩形中的所有灯。
在这里插入图片描述

示例 2:

输入:n = 5, lamps = [[0,0],[4,4]], queries = [[1,1],[1,1]]
输出:[1,1]

示例 3:

输入:n = 5, lamps = [[0,0],[0,4]], queries = [[0,4],[0,1],[1,4]]
输出:[1,1,0]

提示:

1 <= n <= 10^9
0 <= lamps.length <= 20000
0 <= queries.length <= 20000
lamps[i].length == 2
0 <= rowi, coli < n
queries[j].length == 2
0 <= rowj, colj < n

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/grid-illumination
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

主要问题就是怎么表示对角线

class Solution {
    int[][] dir = {{0, 0}, {0,1}, {0, -1}, {1,0}, {-1, 0}, {-1, -1}, {1, -1}, {1, 1}, {-1, 1}};
    public int[] gridIllumination(int n, int[][] lamps, int[][] queries) {
        //想想该怎么记录
        //如果单纯是行和列的话,就可以简单记录每一行一列被照亮的次数
        //然后查询的时候,看这个地方是否被照亮,然后查找这个位置附近的灯,把它熄灭,然后从哈希表中减去
        //有对角线的话,应该怎么表示对角线呢,主对角线从右上角开始记录,副对角线从左上角开始记录
        //主副对角线有2n - 1条
        //怎么统计主对角线和副对角线呢,发现主对角线的特点,i - j 是固定值
        //副对角线,i + j是固定值,这样就可以记录了
        
        int ll = lamps.length;
        int lq = queries.length;

        Map<Integer, Integer> row = new HashMap<>();
        Map<Integer, Integer> col = new HashMap<>();
        Map<Integer, Integer> first = new HashMap<>();      //主对角线
        Map<Integer, Integer> sec = new HashMap<>();        //副对角线

        Set<Long> set = new HashSet<>();     //记录亮灯的地方
        for(int i = 0; i < ll; i++){
            int[] temp = lamps[i];
            
            long idx = temp[0] * 1000000001 + temp[1];
            if(set.contains(idx))
                continue;
            set.add(idx);
            int t1 = temp[0] + temp[1];
            int t2 = temp[0] - temp[1];
            row.put(temp[0], row.getOrDefault(temp[0], 0) + 1);
            col.put(temp[1], col.getOrDefault(temp[1], 0) + 1);
            first.put(t2, first.getOrDefault(t2, 0) + 1);
            sec.put(t1, sec.getOrDefault(t1, 0) + 1);
        }

        int[] res = new int[lq];
        for(int i = 0; i < lq; i++){
            int[] temp = queries[i];
            int t1 = temp[0] + temp[1];
            int t2 = temp[0] - temp[1];
        
            if(row.getOrDefault(temp[0], 0) > 0 || col.getOrDefault(temp[1], 0) > 0 || first.getOrDefault(t2, 0) > 0 || sec.getOrDefault(t1, 0) > 0)
                res[i] = 1;
            else
                res[i] = 0;
            
            for(int[] d : dir){
                int x = temp[0] + d[0];
                int y = temp[1] + d[1];
                long idx = x * 1000000001 + y;
                if(x < 0 || x >= n || y < 0 || y >= n || !set.contains(idx))
                    continue;
                
                //如果这个位置有灯
                int tt1 = x + y;
                int tt2 = x - y;

                set.remove(idx);
                row.put(x, row.get(x) - 1);
                col.put(y, col.get(y) - 1);
                first.put(tt2, first.get(tt2) - 1);
                sec.put(tt1, sec.get(tt1) - 1);
            }    
        }
        return res;

    }
}

2006. 差的绝对值为 K 的数对数目

2022.2.09 每日一题

题目描述

给你一个整数数组 nums 和一个整数 k ,请你返回数对 (i, j) 的数目,满足 i < j 且 |nums[i] - nums[j]| == k 。

|x| 的值定义为:

如果 x >= 0 ,那么值为 x 。
如果 x < 0 ,那么值为 -x 。

示例 1:

输入:nums = [1,2,2,1], k = 1
输出:4
解释:差的绝对值为 1 的数对为:
-[1,2,2,1]
-[1,2,2,1]
-[1,2,2,1]
-[1,2,2,1]

示例 2:

输入:nums = [1,3], k = 3
输出:0
解释:没有任何数对差的绝对值为 3 。

示例 3:

输入:nums = [3,2,1,5,4], k = 2
输出:3
解释:差的绝对值为 2 的数对为:
-[3,2,1,5,4]
-[3,2,1,5,4]
-[3,2,1,5,4]

提示:

1 <= nums.length <= 200
1 <= nums[i] <= 100
1 <= k <= 99

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/count-number-of-pairs-with-absolute-difference-k
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

暴力

class Solution {
    public int countKDifference(int[] nums, int k) {
        //暴力可以,排序可以吗,好像也行,因为是绝对值等于k
        int res = 0;
        int n = nums.length;
        for(int i = 0; i < n; i++){
            for(int j = i + 1; j < n; j++){
                if(Math.abs(nums[i] - nums[j]) == k)
                    res++;
            }
        }
        return res;
    }
}

因为最多只有100种数,所以相当于计数排序

class Solution {
    public int countKDifference(int[] nums, int k) {
        //暴力可以,排序可以吗,好像也行,因为是绝对值等于k
        int res = 0;
        int n = nums.length;
        int[] count = new int[101];
        for(int i = 0; i < n; i++){
            count[nums[i]]++;
        }
        for(int i = 1; i < 101 - k; i++){
            res += count[i] * count[i + k];
        }
        return res;
    }
}

遍历一次,不知道大家是否会有这种感觉,这样到底对吗,后面添加的数不影响结果吗
其实不会影响的,因为每次遍历到一个数,都会计算这个数当前满足条件的个数,因此每个数都能和它需要匹配的数都匹配过,所以不会错

class Solution {
    public int countKDifference(int[] nums, int k) {
        int[] cnts = new int[110];
        int n = nums.length, ans = 0;
        for (int i = 0; i < n; i++) {
            int t = nums[i];
            if (t - k >= 1) ans += cnts[t - k];
            if (t + k <= 100) ans += cnts[t + k];
            cnts[t]++;
        }
        return ans;
    }
}

1447. 最简分数

2022.2.10 每日一题

题目描述

给你一个整数 n ,请你返回所有 0 到 1 之间(不包括 0 和 1)满足分母小于等于 n 的 最简 分数 。分数可以以 任意 顺序返回。

示例 1:

输入:n = 2
输出:[“1/2”]
解释:“1/2” 是唯一一个分母小于等于 2 的最简分数。

示例 2:

输入:n = 3
输出:[“1/2”,“1/3”,“2/3”]

示例 3:

输入:n = 4
输出:[“1/2”,“1/3”,“1/4”,“2/3”,“3/4”]
解释:“2/4” 不是最简分数,因为它可以化简为 “1/2” 。

示例 4:

输入:n = 1
输出:[]

提示:

1 <= n <= 100

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/simplified-fractions
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

主要问题是判断两个数是否有公因数

class Solution {
    public List<String> simplifiedFractions(int n) {
        //相当于分子分母没有除1以外的公因数
        //如何判断两个数是否有公因数
        
        List<String> res = new ArrayList<>();

        for(int i = 2; i <= n; i++){
            for(int j = 1; j < i; j++){
                //如果他俩没有公因数
                if(!judge(i, j)){
                    String temp = j + "/" + i;
                    res.add(temp);
                }
            }
        }
        return res;
    }   
    public boolean judge(int i, int j){
        for(int t = 2; t <= j; t++){
            if(i % t == 0 && j % t == 0)
                return true;
        }
        return false;
    }
}

辗转相除法

class Solution {
    public List<String> simplifiedFractions(int n) {
        //相当于分子分母没有除1以外的公因数
        //如何判断两个数是否有公因数
        
        List<String> res = new ArrayList<>();

        for(int i = 2; i <= n; i++){
            for(int j = 1; j < i; j++){
                //如果他俩没有公因数
                if(judge(i, j) == 1){
                    String temp = j + "/" + i;
                    res.add(temp);
                }
            }
        }
        return res;
    }   

    //写一下辗转相除法
    public int judge(int i, int j){
        while(i % j != 0){
            int yu = i % j;
            i = j;
            j = yu;
        }
        return j;
    }
	//递归形式
	/*
	public int judge(int i, int j){
        return i % j == 0 ? j : judge(j, i % j);
    }
	*/
}
### 回答1: 好的,我来用中文回复这个链接:https://leetcode-cn.com/tag/dynamic-programming/ 这个链接是 LeetCode 上关于动态规划的题目集合。动态规划是一种常用的算法思想,可以用来解决很多实际问题,比如最长公共子序列、背包问题、最短路径等等。在 LeetCode 上,动态规划也是一个非常重要的题型,很多题目都需要用到动态规划的思想来解决。 这个链接里包含了很多关于动态规划的题目,按照难度从简单到困难排列。每个题目都有详细的题目描述、输入输出样例、题目解析和代码实现等内容,非常适合想要学习动态规划算法的人来练习和提高自己的能力。 总之,这个链接是一个非常好的学习动态规划算法的资源,建议大家多多利用。 ### 回答2: 动态规划是一种算法思想,通常用于优化具有重叠子问题和最优子结构性质的问题。由于其成熟的学理论和强大的实用效果,动态规划在计算机科学、学、经济学、管理学等领域均有重要应用。 在计算机科学领域,动态规划常用于解决最优化问题,如背包问题、图像处理、语音识别、自然语言处理等。同时,在计算机网络和分布式系统中,动态规划也广泛应用于各种优化算法中,如链路优化、路由算法、网络流量控制等。 对于算法领域的程序员而言,动态规划是一种必要的技能和知识点。在LeetCode这样的程序员平台上,题目分类和标签设置十分细致和方便,方便程序员查找并深入学习不同类型的算法。 LeetCode的动态规划标签下的题目涵盖了各种难度级别和场景的问题。从简单的斐波那契列、迷宫问题到可以用于实际应用的背包问题、最长公共子序列等,难度不断递进且话题丰富,有助于开发人员掌握动态规划的实际应用技能和抽象思维模式。 因此,深入LeetCode动态规划分类下的题目学习和练习,对于程序员的职业发展和技能提升有着重要的意义。 ### 回答3: 动态规划是一种常见的算法思想,它通过将问题拆分成子问题的方式进行求解。在LeetCode中,动态规划标签涵盖了众多经典和优美的算法问题,例如斐波那契列、矩阵链乘法、背包问题等。 动态规划的核心思想是“记忆化搜索”,即将中间状态保存下来,避免重复计算。通常情况下,我们会使用一张二维表来记录状态转移过程中的中间值,例如动态规划求解斐波那契列问题时,就可以定义一个二维组f[i][j],代表第i项斐波那契列中,第j个元素的值。 在LeetCode中,动态规划标签下有众多难度不同的问题。例如,经典的“爬楼梯”问题,要求我们计算到n级楼梯的方案。这个问题的解法非常简单,只需要维护一个长度为n的组,记录到达每一级楼梯的方案即可。类似的问题还有“零钱兑换”、“乘积最大子组”、“通配符匹配”等,它们都采用了类似的动态规划思想,通过拆分问题、保存中间状态来求解问题。 需要注意的是,动态规划算法并不是万能的,它虽然可以处理众多经典问题,但在某些场景下并不适用。例如,某些问题的状态转移过程比较复杂,或者状态转移方程中存在多个参,这些情况下使用动态规划算法可能会变得比较麻烦。此外,动态规划算法也存在一些常见误区,例如错用贪心思想、未考虑边界情况等。 总之,掌握动态规划算法对于LeetCode的学习和解题都非常重要。除了刷题以外,我们还可以通过阅读经典的动态规划书籍,例如《算法竞赛进阶指南》、《算法与据结构基础》等,来深入理解这种算法思想。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值