Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 82efcfa

Browse files
committed
1703-1728-1735-1739-1745-1761-1830 (7)
1 parent cd47649 commit 82efcfa

14 files changed

+673
-0
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import java.util.ArrayList;
2+
import java.util.List;
3+
4+
public class Solution1703 {
5+
public int minMoves(int[] nums, int k) {
6+
List<Integer> p = new ArrayList<>();
7+
for (int i = 0; i < nums.length; ++i) {
8+
if (nums[i] != 0) p.add(i - p.size());
9+
}
10+
int m = p.size();
11+
int[] ps = new int[m + 1]; // p 的前缀和
12+
for (int i = 0; i < m; i++) {
13+
ps[i + 1] = ps[i] + p.get(i);
14+
}
15+
16+
int ans = Integer.MAX_VALUE;
17+
for (int i = 0; i <= m - k; ++i) { // p[i] 到 p[i+k-1] 中所有数到 p[i+k/2] 的距离之和,取最小值
18+
ans = Math.min(ans, ps[i] + ps[i + k] - ps[i + k / 2] * 2 - p.get(i + k / 2) * (k % 2));
19+
}
20+
return ans;
21+
}
22+
}
23+
/*
24+
1703. 得到连续 K 个 1 的最少相邻交换次数
25+
https://leetcode.cn/problems/minimum-adjacent-swaps-for-k-consecutive-ones/description/
26+
27+
给你一个整数数组 nums 和一个整数 k 。 nums 仅包含 0 和 1 。每一次移动,你可以选择 相邻 两个数字并将它们交换。
28+
请你返回使 nums 中包含 k 个 连续 1 的 最少 交换次数。
29+
提示:
30+
1 <= nums.length <= 10^5
31+
nums[i] 要么是 0 ,要么是 1 。
32+
1 <= k <= sum(nums)
33+
34+
转换成中位数贪心+空间优化 https://leetcode.cn/problems/minimum-adjacent-swaps-for-k-consecutive-ones/solutions/2024387/tu-jie-zhuan-huan-cheng-zhong-wei-shu-ta-iz4v/
35+
时间复杂度 O(n)。
36+
*/
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
import java.util.ArrayDeque;
2+
import java.util.ArrayList;
3+
import java.util.Arrays;
4+
import java.util.List;
5+
import java.util.Queue;
6+
7+
public class Solution1728 {
8+
private static final int[][] DIRS = {{0, -1}, {0, 1}, {-1, 0}, {1, 0}}; // 左右上下
9+
10+
public boolean canMouseWin(String[] grid, int catJump, int mouseJump) {
11+
int m = grid.length, n = grid[0].length();
12+
// 鼠和猫分别建图
13+
List<Integer>[] gMouse = new ArrayList[m * n];
14+
List<Integer>[] gCat = new ArrayList[m * n];
15+
Arrays.setAll(gMouse, i -> new ArrayList<>());
16+
Arrays.setAll(gCat, i -> new ArrayList<>());
17+
int mx = 0, my = 0, cx = 0, cy = 0, fx = 0, fy = 0;
18+
for (int i = 0; i < m; i++) {
19+
for (int j = 0; j < n; j++) {
20+
char c = grid[i].charAt(j);
21+
if (c == '#') { // 墙
22+
continue;
23+
}
24+
if (c == 'M') { // 鼠的位置
25+
mx = i;
26+
my = j;
27+
} else if (c == 'C') { // 猫的位置
28+
cx = i;
29+
cy = j;
30+
} else if (c == 'F') { // 食物(洞)的位置
31+
fx = i;
32+
fy = j;
33+
}
34+
int v = i * n + j; // 二维坐标 (i,j) 映射为一维坐标 v
35+
for (int[] dir : DIRS) { // 枚举左右上下四个方向
36+
for (int k = 0; k <= mouseJump; k++) { // 枚举跳跃长度
37+
int x = i + k * dir[0], y = j + k * dir[1];
38+
if (x < 0 || x >= m || y < 0 || y >= n || grid[x].charAt(y) == '#') { // 出界或者遇到墙
39+
break;
40+
}
41+
gMouse[v].add(x * n + y); // 连边
42+
}
43+
for (int k = 0; k <= catJump; k++) { // 枚举跳跃长度
44+
int x = i + k * dir[0], y = j + k * dir[1];
45+
if (x < 0 || x >= m || y < 0 || y >= n || grid[x].charAt(y) == '#') { // 出界或者遇到墙
46+
break;
47+
}
48+
gCat[v].add(x * n + y); // 连边
49+
}
50+
}
51+
}
52+
}
53+
54+
// 判断是否鼠赢
55+
return catMouseGame(gMouse, gCat, mx * n + my, cx * n + cy, fx * n + fy) == 1;
56+
}
57+
58+
// 913. 猫和老鼠
59+
private int catMouseGame(List<Integer>[] gMouse, List<Integer>[] gCat, int mouseStart, int catStart, int hole) {
60+
int n = gMouse.length;
61+
int[][][] deg = new int[n][n][2];
62+
for (int i = 0; i < n; i++) {
63+
for (int j = 0; j < n; j++) {
64+
deg[i][j][0] = gMouse[i].size();
65+
deg[i][j][1] = gCat[j].size();
66+
}
67+
}
68+
69+
int[][][] winner = new int[n][n][2];
70+
Queue<int[]> q = new ArrayDeque<>();
71+
for (int i = 0; i < n; i++) {
72+
winner[hole][i][1] = 1; // 鼠到达洞中(此时轮到猫移动),鼠获胜
73+
winner[i][hole][0] = 2; // 猫到达洞中(此时轮到鼠移动),猫获胜
74+
winner[i][i][0] = winner[i][i][1] = 2; // 猫和鼠出现在同一个节点,无论轮到谁移动,都是猫获胜
75+
q.offer(new int[]{hole, i, 1}); // 从终点开始倒推
76+
q.offer(new int[]{i, hole, 0});
77+
q.offer(new int[]{i, i, 0});
78+
q.offer(new int[]{i, i, 1});
79+
}
80+
81+
while (!q.isEmpty()) {
82+
int[] cur = q.poll();
83+
int mouse = cur[0], cat = cur[1], turn = cur[2];
84+
int win = winner[mouse][cat][turn]; // 最终谁赢了
85+
for (int[] pre : getPreStates(mouse, cat, turn, gMouse, gCat, winner)) {
86+
int preMouse = pre[0], preCat = pre[1], preTurn = turn ^ 1;
87+
// 情况一:如果上一回合鼠从 pre 移动到 cur,最终鼠赢,那么标记 pre 状态的 winner = 鼠
88+
// 情况二:如果上一回合猫从 pre 移动到 cur,最终猫赢,那么标记 pre 状态的 winner = 猫
89+
// 情况三:如果上一回合鼠从 pre 移动到 cur,最终猫赢,那么待定,直到我们发现从 pre 出发能到达的状态都是猫赢,那么标记 pre 状态的 winner = 猫
90+
// 情况四:如果上一回合猫从 pre 移动到 cur,最终鼠赢,那么待定,直到我们发现从 pre 出发能到达的状态都是鼠赢,那么标记 pre 状态的 winner = 鼠
91+
if (preTurn == win - 1 || --deg[preMouse][preCat][preTurn] == 0) {
92+
winner[preMouse][preCat][preTurn] = win;
93+
q.offer(new int[]{preMouse, preCat, preTurn}); // 继续倒推
94+
}
95+
}
96+
}
97+
98+
// 鼠在节点 mouseStart,猫在节点 catStart,当前轮到鼠移动
99+
return winner[mouseStart][catStart][0]; // 返回最终谁赢了(或者平局)
100+
}
101+
102+
// 获取 (mouse, cat, turn) 的上个状态(值尚未确定)
103+
private List<int[]> getPreStates(int mouse, int cat, int turn, List<Integer>[] gMouse, List<Integer>[] gCat, int[][][] winner) {
104+
List<int[]> preStates = new ArrayList<>();
105+
if (turn == 0) { // 当前轮到鼠移动
106+
for (int preCat : gCat[cat]) { // 上一轮猫的位置
107+
if (winner[mouse][preCat][1] == 0) { // 猫无法移动到洞中
108+
preStates.add(new int[]{mouse, preCat});
109+
}
110+
}
111+
} else { // 当前轮到猫移动
112+
for (int preMouse : gMouse[mouse]) { // 上一轮鼠的位置
113+
if (winner[preMouse][cat][0] == 0) {
114+
preStates.add(new int[]{preMouse, cat});
115+
}
116+
}
117+
}
118+
return preStates;
119+
}
120+
}
121+
/*
122+
1728. 猫和老鼠 II
123+
https://leetcode.cn/problems/cat-and-mouse-ii/description/
124+
125+
一只猫和一只老鼠在玩一个叫做猫和老鼠的游戏。
126+
它们所处的环境设定是一个 rows x cols 的方格 grid ,其中每个格子可能是一堵墙、一块地板、一位玩家(猫或者老鼠)或者食物。
127+
- 玩家由字符 'C' (代表猫)和 'M' (代表老鼠)表示。
128+
- 地板由字符 '.' 表示,玩家可以通过这个格子。
129+
- 墙用字符 '#' 表示,玩家不能通过这个格子。
130+
- 食物用字符 'F' 表示,玩家可以通过这个格子。
131+
- 字符 'C' , 'M' 和 'F' 在 grid 中都只会出现一次。
132+
猫和老鼠按照如下规则移动:
133+
- 老鼠 先移动 ,然后两名玩家轮流移动。
134+
- 每一次操作时,猫和老鼠可以跳到上下左右四个方向之一的格子,他们不能跳过墙也不能跳出 grid 。
135+
- catJump 和 mouseJump 是猫和老鼠分别跳一次能到达的最远距离,它们也可以跳小于最大距离的长度。
136+
- 它们可以停留在原地。
137+
- 老鼠可以跳跃过猫的位置。
138+
游戏有 4 种方式会结束:
139+
- 如果猫跟老鼠处在相同的位置,那么猫获胜。
140+
- 如果猫先到达食物,那么猫获胜。
141+
- 如果老鼠先到达食物,那么老鼠获胜。
142+
- 如果老鼠不能在 1000 次操作以内到达食物,那么猫获胜。
143+
给你 rows x cols 的矩阵 grid 和两个整数 catJump 和 mouseJump ,双方都采取最优策略,如果老鼠获胜,那么请你返回 true ,否则返回 false 。
144+
提示:
145+
rows == grid.length
146+
cols = grid[i].length
147+
1 <= rows, cols <= 8
148+
grid[i][j] 只包含字符 'C' ,'M' ,'F' ,'.' 和 '#' 。
149+
grid 中只包含一个 'C' ,'M' 和 'F' 。
150+
1 <= catJump, mouseJump <= 8
151+
152+
逆向思维 + 拓扑序 DP,复用 913 题代码 https://leetcode.cn/problems/cat-and-mouse-ii/solutions/3070697/ni-xiang-si-wei-tuo-bu-xu-dpfu-yong-913-t99rl/
153+
时间复杂度:O(m^2 * n^2 * (m+n))。
154+
相似题目: 913. 猫和老鼠
155+
https://leetcode.cn/problems/cat-and-mouse/
156+
*/
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
public class Solution1735 {
2+
public int[] waysToFillArray(int[][] queries) {
3+
int[] ans = new int[queries.length];
4+
for (int i = 0; i < queries.length; i++) {
5+
int[] q = queries[i];
6+
int n = q[0];
7+
int k = q[1];
8+
long res = 1;
9+
while (k > 1) {
10+
int p = LPF[k];
11+
int e = 1;
12+
for (k /= p; k % p == 0; k /= p) {
13+
e++;
14+
}
15+
res = res * comb(e + n - 1, e) % MOD;
16+
}
17+
ans[i] = (int) res;
18+
}
19+
return ans;
20+
}
21+
22+
static int MOD = (int) 1e9 + 7, MX = (int) 1e5 + 5;
23+
static long[] F = new long[MX + 1], invF = new long[MX + 1];
24+
static int[] LPF = new int[MX]; // i 的最小质因子是 lpf[i]
25+
26+
static {
27+
F[0] = F[1] = invF[0] = invF[1] = 1;
28+
for (int i = 2; i <= MX; i++) F[i] = F[i - 1] * i % MOD;
29+
invF[MX] = quickPow(F[MX], MOD - 2);
30+
for (int i = MX - 1; i >= 2; i--) invF[i] = invF[i + 1] * (i + 1) % MOD;
31+
32+
for (int i = 2; i < MX; i++) {
33+
if (LPF[i] == 0) { // i 是质数
34+
for (int j = i; j < MX; j += i) {
35+
if (LPF[j] == 0) {
36+
LPF[j] = i; // j 的最小质因子是 i
37+
}
38+
}
39+
}
40+
}
41+
}
42+
43+
static long comb(int n, int m) {
44+
if (n < m || m < 0) return 0;
45+
return F[n] * invF[n - m] % MOD * invF[m] % MOD;
46+
}
47+
48+
static long quickPow(long a, long b) {
49+
long res = 1L;
50+
while (b > 0) {
51+
if ((b & 1) != 0) res = res * a % MOD;
52+
a = a * a % MOD;
53+
b >>= 1;
54+
}
55+
return res;
56+
}
57+
}
58+
/*
59+
1735. 生成乘积数组的方案数
60+
https://leetcode.cn/problems/count-ways-to-make-array-with-product/description/
61+
62+
给你一个二维整数数组 queries ,其中 queries[i] = [ni, ki] 。第 i 个查询 queries[i] 要求构造长度为 ni 、每个元素都是正整数的数组,且满足所有元素的乘积为 ki ,请你找出有多少种可行的方案。由于答案可能会很大,方案数需要对 109 + 7 取余 。
63+
请你返回一个整数数组 answer,满足 answer.length == queries.length ,其中 answer[i]是第 i 个查询的结果。
64+
提示:
65+
1 <= queries.length <= 10^4
66+
1 <= ni, ki <= 10^4
67+
68+
质因子分解+放球问题 https://leetcode.cn/problems/count-ways-to-make-array-with-product/solutions/2713481/tu-jie-zhi-yin-zi-fen-jie-fang-qiu-wen-t-fboo/
69+
时间复杂度:预处理的时间为 O(N+KloglogK),其中 N=10^4, K=10^4。回答单个询问的时间为 O(logk),因为 k 有 O(logk) 个质因子。
70+
*/
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
public class Solution1739 {
2+
// O(sqrt(n))
3+
public int minimumBoxes(int n) {
4+
int ans = 0;
5+
int maxN = 0;
6+
for (int i = 1; maxN + ans + i <= n; i++) {
7+
ans += i;
8+
maxN += ans;
9+
}
10+
for (int j = 1; maxN < n; j++) {
11+
ans++;
12+
maxN += j;
13+
}
14+
return ans;
15+
}
16+
17+
// O(1)
18+
public int minimumBoxes2(int n) {
19+
int x = (int) Math.cbrt(6L * n);
20+
int ans = x * (x + 1) / 2;
21+
int maxN = (int) ((long) x * (x + 1) * (x + 2) / 6);
22+
if (maxN > n) {
23+
maxN -= ans;
24+
ans -= x;
25+
}
26+
return ans + (int) Math.ceil(Math.sqrt(1 + 8 * (n - maxN))) / 2;
27+
}
28+
}
29+
/*
30+
1739. 放置盒子
31+
https://leetcode.cn/problems/building-boxes/description/
32+
33+
有一个立方体房间,其长度、宽度和高度都等于 n 个单位。请你在房间里放置 n 个盒子,每个盒子都是一个单位边长的立方体。放置规则如下:
34+
- 你可以把盒子放在地板上的任何地方。
35+
- 如果盒子 x 需要放置在盒子 y 的顶部,那么盒子 y 竖直的四个侧面都 必须 与另一个盒子或墙相邻。
36+
给你一个整数 n ,返回接触地面的盒子的 最少 可能数量。
37+
提示:
38+
1 <= n <= 10^9
39+
40+
找规律 / 数学。
41+
*/
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
public class Solution1745 {
2+
public boolean checkPartitioning(String s) {
3+
return palindromePartition(s, 3) == 0;
4+
}
5+
6+
// 1278. 分割回文串 III
7+
private int palindromePartition(String S, int k) {
8+
char[] s = S.toCharArray();
9+
int n = s.length;
10+
int[][] minChange = new int[n][n];
11+
for (int i = n - 2; i >= 0; i--) {
12+
for (int j = i + 1; j < n; j++) {
13+
minChange[i][j] = minChange[i + 1][j - 1] + (s[i] != s[j] ? 1 : 0);
14+
}
15+
}
16+
17+
int[] f = minChange[0];
18+
for (int i = 1; i < k; i++) {
19+
for (int r = n - k + i; r >= i; r--) {
20+
f[r] = Integer.MAX_VALUE;
21+
for (int l = i; l <= r; l++) {
22+
f[r] = Math.min(f[r], f[l - 1] + minChange[l][r]);
23+
}
24+
}
25+
}
26+
return f[n - 1];
27+
}
28+
}
29+
/*
30+
1745. 分割回文串 IV
31+
https://leetcode.cn/problems/palindrome-partitioning-iv/description/
32+
33+
给你一个字符串 s ,如果可以将它分割成三个 非空 回文子字符串,那么返回 true ,否则返回 false 。
34+
当一个字符串正着读和反着读是一模一样的,就称其为 回文字符串 。
35+
提示:
36+
3 <= s.length <= 2000
37+
s 只包含小写英文字母。
38+
39+
相似题目: 1278. 分割回文串 III
40+
https://leetcode.cn/problems/palindrome-partitioning-iii/
41+
*/

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /