LeetCode 961. 在长度 2N 的数组中找出重复 N 次的元素 / 464. 我能赢吗(博弈) / 675. 为高尔夫比赛砍树

961. 在长度 2N 的数组中找出重复 N 次的元素

2022.5.21 每日一题

题目描述

给你一个整数数组 nums ,该数组具有以下属性:

  • nums.length == 2 * n.
  • nums 包含 n + 1 个 不同的 元素
  • nums 中恰有一个元素重复 n 次

找出并返回重复了 n 次的那个元素。

示例 1:

输入:nums = [1,2,3,3]
输出:3

示例 2:

输入:nums = [2,1,2,5,3,2]
输出:2

示例 3:

输入:nums = [5,1,5,2,5,3,5,4]
输出:5

提示:

2 <= n <= 5000
nums.length == 2 * n
0 <= nums[i] <= 10^4
nums 由 n + 1 个 不同的 元素组成,且其中一个元素恰好重复 n 次

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

思路

想想有没有什么空间复杂度低的解法

class Solution {
    public int repeatedNTimes(int[] nums) {
        //因为相同的数有n个,如果不是相邻那么只能隔一个,
        //所以遍历整个数组,看看有没有相邻的或者隔一个相同的
        int n = nums.length;
        for(int i = 0; i < n; i++){
            if(nums[i] == nums[(i + 1) % n])
                return nums[i];
            if(nums[i] == nums[(i + 2) % n])
                return nums[i];
        }
        return -1;
    }
}

464. 我能赢吗

2022.5.22 每日一题

题目描述

在 “100 game” 这个游戏中,两名玩家轮流选择从 1 到 10 的任意整数,累计整数和,先使得累计整数和 达到或超过 100 的玩家,即为胜者。

如果我们将游戏规则改为 “玩家 不能 重复使用整数” 呢?

例如,两个玩家可以轮流从公共整数池中抽取从 1 到 15 的整数(不放回),直到累计整数和 >= 100。

给定两个整数 maxChoosableInteger (整数池中可选择的最大数)和 desiredTotal(累计和),若先出手的玩家是否能稳赢则返回 true ,否则返回 false 。假设两位玩家游戏时都表现 最佳 。

示例 1:

输入:maxChoosableInteger = 10, desiredTotal = 11
输出:false
解释:
无论第一个玩家选择哪个整数,他都会失败。
第一个玩家可以选择从 1 到 10 的整数。
如果第一个玩家选择 1,那么第二个玩家只能选择从 2 到 10 的整数。
第二个玩家可以通过选择整数 10(那么累积和为 11 >= desiredTotal),从而取得胜利.
同样地,第一个玩家选择任意其他整数,第二个玩家都会赢。

示例 2:

输入:maxChoosableInteger = 10, desiredTotal = 0
输出:true

示例 3:

输入:maxChoosableInteger = 10, desiredTotal = 1
输出:true

提示:

1 <= maxChoosableInteger <= 20
0 <= desiredTotal <= 300

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

思路

这里提前总和的判断很有必要,否则会出现问题,因为两个人都不能达到目标的时候,第一个人是输的情况,所以这里必须提前判断
因为这个博弈题里面没有必胜的状态,所以结合数据范围确定要遍历所有情况
第二个关键点就是最大数是20,所以确定可以用状态压缩
最关键的点就是想明白双方的关系,后手不能赢,那么先手胜利;在递归的时候,不要因为两个人轮流选择而搞晕,只需要判断当前谁赢就可以了

class Solution {
    int[] used = new int[1 << 20];
    public boolean canIWin(int maxChoosableInteger, int desiredTotal) {
        //又是这种博弈的题,但是因为数不多,是否可以遍历所有情况搞呢
        //先用一个20位的数表示哪些数被选过了或者没有选,即状态压缩的思想
        //遍历所有情况
        if(maxChoosableInteger >= desiredTotal)
            return true;
        if((1 + maxChoosableInteger) * maxChoosableInteger / 2 < desiredTotal)
            return false;
        return dfs(0, 0, maxChoosableInteger, desiredTotal);
    }

    public boolean dfs(int sum, int cur, int maxChoosableInteger, int desiredTotal){
        //如果遍历过且为-1,那么是输的情况;1为赢的情况
        if(used[cur] == -1)
            return false;
        else if(used[cur] == 1)
            return true;
        //遍历所有情况
        for(int i = 1; i <= maxChoosableInteger; i++){
            //如果被选择过,跳过
            if(((cur >> (i - 1)) & 1) == 1)
                continue;
            //如果超过目标值了,那么返回true
            if(sum + i >= desiredTotal){
                used[cur] = 1;
                return true;
            }

            //如果选择了x之后,对方一定输的情况也是true
            boolean temp = dfs(sum + i, (cur ^ (1 << (i - 1))), maxChoosableInteger, desiredTotal);
            if(!temp){
                used[cur] = 1;
                return true;
            }
        }
        used[cur] = -1;
        return false;
    }
}

675. 为高尔夫比赛砍树

2022.5.23 每日一题

题目描述

你被请来给一个要举办高尔夫比赛的树林砍树。树林由一个 m x n 的矩阵表示, 在这个矩阵中:

  • 0 表示障碍,无法触碰
  • 1 表示地面,可以行走
  • 比 1 大的数 表示有树的单元格,可以行走,数值表示树的高度

每一步,你都可以向上、下、左、右四个方向之一移动一个单位,如果你站的地方有一棵树,那么你可以决定是否要砍倒它。

你需要按照树的高度从低向高砍掉所有的树,每砍过一颗树,该单元格的值变为 1(即变为地面)。

你将从 (0, 0) 点开始工作,返回你砍完所有树需要走的最小步数。 如果你无法砍完所有的树,返回 -1 。

可以保证的是,没有两棵树的高度是相同的,并且你至少需要砍倒一棵树。

示例 1:

在这里插入图片描述
输入:forest = [[1,2,3],[0,0,4],[7,6,5]]
输出:6
解释:沿着上面的路径,你可以用 6 步,按从最矮到最高的顺序砍掉这些树。

示例 2:

在这里插入图片描述
输入:forest = [[1,2,3],[0,0,0],[7,6,5]]
输出:-1
解释:由于中间一行被障碍阻塞,无法访问最下面一行中的树。

示例 3:

输入:forest = [[2,3,4],[0,0,5],[8,7,6]]
输出:6
解释:可以按与示例 1 相同的路径来砍掉所有的树。
(0,0) 位置的树,可以直接砍去,不用算步数。

提示:

m == forest.length
n == forest[i].length
1 <= m, n <= 50
0 <= forest[i][j] <= 10^9

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

思路

其实题目中给了很明显的提示
需要从低到高砍树,而且需要砍掉所有树
所以从低到高将所有树排序,然后找到两个点之间的最短路就可以了
找最短路用bfs

class Solution {
    int[][] fst;
    int m;
    int n;
    int[][] dirs = {{0,1}, {0,-1}, {1,0}, {-1,0}};
    public int cutOffTree(List<List<Integer>> forest) {
        //因为需要砍的时候是从低向高砍掉所有树,并且需要砍掉所有树
        //所以每次走的时候,是找最低的树
        //所以自然想到了把所有的树存到一个集合中,并且排序,
        //然后任务就是怎么把这些点连接起来

        
        m = forest.size();
        n = forest.get(0).size();
        int[][] tree = new int[m * n][3];
        fst = new int[m][n];
        int idx = 0;
        for(int i = 0; i < m; i++){
            for(int j = 0; j < n; j++){
                fst[i][j] = forest.get(i).get(j);
                int height = fst[i][j];
                if(height > 1){
                    tree[idx][0] = height;
                    tree[idx][1] = i;
                    tree[idx][2] = j;
                    idx++;
                }
            }
        }
        int[][] tree2 = new int[idx][3];
        System.arraycopy(tree, 0, tree2, 0, idx); 
        Arrays.sort(tree2, (a, b) -> (a[0] - b[0]));
        
        /*
        for(int i = 0; i < idx; i++){
            System.out.println(tree2[i][0] + "," + tree2[i][1] + "," + tree2[i][2]);
        }
        */
        
        
        int[] start = new int[]{0, 0};
        int res = 0;
        //排序以后就是找两点的最短路了
        for(int i = 0; i < idx; i++){
            int[] end = new int[]{tree2[i][1], tree2[i][2]};
            int len = leastPath(start, end);
            //System.out.println(len);
            //如果两点不可达,返回-1
            if(len < 0)
                return -1;
            
            res += len;
            start = end;
        }
        
        return res;
    }

    public int leastPath(int[] start, int[] end){
        int len = 0;
        Queue<int[]> queue = new LinkedList<>();
        boolean[][] used = new boolean[m][n];
        used[start[0]][start[1]] = true;

        queue.offer(start);
        while(!queue.isEmpty()){
            int size = queue.size();
            while(size-- > 0){
                int[] top = queue.poll();
                if(top[0] == end[0] && top[1] == end[1])
                    return len;
                for(int[] dir : dirs){
                    int x = top[0] + dir[0];
                    int y = top[1] + dir[1];
                    if(x < 0 || x >= m || y < 0 || y >= n || fst[x][y] == 0 || used[x][y])
                        continue;
                    used[x][y] = true;
                    queue.offer(new int[]{x, y});
                }
            }
            len++;
        }
        return -1;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值