LeetCode 961. 在长度 2N 的数组中找出重复 N 次的元素 / 464. 我能赢吗(博弈) / 675. 为高尔夫比赛砍树
Posted Zephyr丶J
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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;
以上是关于LeetCode 961. 在长度 2N 的数组中找出重复 N 次的元素 / 464. 我能赢吗(博弈) / 675. 为高尔夫比赛砍树的主要内容,如果未能解决你的问题,请参考以下文章
LeetCode 961. 在长度 2N 的数组中找出重复 N 次的元素 / 464. 我能赢吗(博弈) / 675. 为高尔夫比赛砍树
「 每日一练,快乐水题 」961. 在长度 2N 的数组中找出重复 N 次的元素
「 每日一练,快乐水题 」961. 在长度 2N 的数组中找出重复 N 次的元素