第三十二天
1020 飞地的数量
给你一个大小为 m x n 的二进制矩阵 grid ,其中 0 表示一个海洋单元格、1 表示一个陆地单元格。
一次 移动 是指从一个陆地单元格走到另一个相邻(上、下、左、右)的陆地单元格或跨过 grid 的边界。
返回网格中 无法 在任意次数的移动中离开网格边界的陆地单元格的数量。
方法
使用深搜DFS来解决这个问题,题目要求我们找到不能够连通到外界的1的数量,那么我们可以再遍历的过程中统计一下经过的格子的数量,同时维护一个flag变量,当flag为true时表明能够到达边界,那么当我们返回结果的时候就不将这一次的累加结果放入答案,否则就将统计的数量加入到答案中去,最后我们直接返回答案即可。
class Solution {
public static int[] dx = new int[]{0, 0, 1, -1};
public static int[] dy = new int[]{1, -1, 0, 0};
public static int xLength;
public static int yLength;
public static boolean[][] isVisited;
public static boolean flag;
public static int cnt;
public int numEnclaves(int[][] grid) {
int ans = 0;
xLength = grid.length;
yLength = grid[0].length;
isVisited = new boolean[xLength][yLength];
for (int i = 0; i < xLength; ++i){
for (int j = 0; j < yLength; ++j){
if (!isVisited[i][j] && grid[i][j] == 1){
flag = false;
cnt = 0;
deepFirstSearch(grid, i, j);
if (!flag) ans += cnt;
}
}
}
return ans;
}
public static void deepFirstSearch(int[][] grid, int x, int y){
isVisited[x][y] = true;
cnt++;
for (int i = 0; i < 4; ++i){
int newX = x + dx[i];
int newY = y + dy[i];
if (!(newX >= 0 && newX < xLength && newY >= 0 && newY < yLength)) flag = true;
else{
if (grid[newX][newY] == 1 && !isVisited[newX][newY]) {
deepFirstSearch(grid, newX, newY);
}
}
}
}
}
72 编辑距离
给你两个单词 word1 和 word2, 请返回将 word1 转换成 word2 所使用的最少操作数。
你可以对一个单词进行如下三种操作:
- 插入一个字符
- 删除一个字符
- 替换一个字符
方法
考虑如下情况,如果我们已经知道了两个字符串a和b,把a编辑成b所需要的最少次数是k,那么此时,如果我们分别向a和b后面追加一个字符x和字符y,x和y存在以下四种情况:
x==y,此时我们不需要做任何操作,即a+x和b+y所需要的编辑次数为kx==''且y!='',此时,如果我们需要将a+x编辑成为b+y,那么所需要的最少次数就是k+1,即我们需要向b尾部插入一个字符y,操作次数会增加1x!=''且y=='',同理次数的最少编辑次数为k+1,我们需要将a+x尾部的字符x删除掉,因此将a+x编辑为b+y所需要的最少需要增加1x!=y,此时我们如果要将a+x编辑为b+y,我们需要将字符x替换为y即可,最少的操作次数也是k+1
基于上述情况,我们定义dp数组,令dp[i][j]表示将 第一个字符串从0位置到i位置的子串 编辑成为 第二个字符串 从0位置到j位置的子串 所需要的最少操作次数,则状态转移方程为:
d
p
[
i
]
[
j
]
=
{
d
p
[
i
−
1
]
[
j
−
1
]
s
1
[
i
]
=
s
2
[
j
]
m
i
n
{
d
p
[
i
−
1
]
[
j
]
+
1
,
d
p
[
i
]
[
j
−
1
]
+
1
,
d
p
[
i
−
1
]
[
j
−
1
]
+
1
}
e
l
s
e
dp[i][j]= \left\{ \begin{aligned} &dp[i-1][j-1] \quad s_1[i]=s_2[j]\\ &min\{dp[i-1][j] + 1,dp[i][j-1] + 1,dp[i-1][j-1]+1\} \quad else\\ \end{aligned} \right.
dp[i][j]={dp[i−1][j−1]s1[i]=s2[j]min{dp[i−1][j]+1,dp[i][j−1]+1,dp[i−1][j−1]+1}else
class Solution {
public int minDistance(String word1, String word2) {
int l1 = word1.length();
int l2 = word2.length();
int[][] dp = new int[l1 + 1][l2 + 1];
for (int i = 0; i <= l1; ++i) dp[i][0] = i;
for (int j = 0; j <= l2; ++j) dp[0][j] = j;
for (int i = 1; i <= l1; ++i){
for (int j = 1; j <= l2; ++j){
if (word1.charAt(i - 1) == word2.charAt(j - 1)){
dp[i][j] = dp[i - 1][j - 1];
}
else{
dp[i][j] = Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1);
dp[i][j] = Math.min(dp[i][j], dp[i - 1][j - 1] + 1);
}
}
}
return dp[l1][l2];
}
}
322 零钱兑换
给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。
计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。
你可以认为每种硬币的数量是无限的。
方法
首先考虑如下情况,如果我们已经知道了组成0->k面值所需要的最少的硬币数量,则如果我们想组成k+1的面值,那么其所需要的最少的硬币数量就是 组成x面值所需要的最少硬币数量 加上 组成y面值所需要的最少硬币数量 的和,其中x+y=k+1,我们去遍历x,找出其中的最小值,就是组成k+1面值的最少硬币数量。
为此,我们定义dp[i]表示组成面值i所需要的最少硬币数量,对于每一个面值i,我们用所所有的硬币面值去试,找出能够让dp[i]达到最少的数量的值作为dp[i]的值,即状态转移方程为:
d
p
[
i
]
=
m
i
n
(
d
p
[
i
−
j
]
+
1
)
i
f
i
−
j
>
=
0
dp[i]=min(dp[i-j]+1) \quad if \quad i-j>=0
dp[i]=min(dp[i−j]+1)ifi−j>=0
class Solution {
public int coinChange(int[] coins, int amount) {
int[] dp = new int[amount + 1];
for (int i = 1; i <= amount; ++i){
dp[i] = Integer.MAX_VALUE;
for (int j : coins){
if (i - j >= 0 && dp[i - j] != Integer.MAX_VALUE) dp[i] = Math.min(dp[i - j] + 1, dp[i]);
}
}
return dp[amount] == Integer.MAX_VALUE ? -1 : dp[amount];
}
}
343 整数拆分
给定一个正整数 n ,将其拆分为 k 个 正整数 的和( k >= 2 ),并使这些整数的乘积最大化。
返回 你可以获得的最大乘积 。
方法
如果我们已经知道了j的拆分最大值,那么对于i=j+i-j拆分,就是j和i-j的拆分的最大值。定义dp[i]表示i的拆分最大值,则状态转移方程如下:
d
p
[
i
]
=
m
a
x
(
d
p
[
j
]
∗
d
p
[
i
−
j
]
)
dp[i]=max(dp[j]*dp[i-j])
dp[i]=max(dp[j]∗dp[i−j])
初始状态,我们令dp[i]=i即可,但是需要考虑特殊情况n=2和n=3的情况,它们的答案我们让其返回1和2。
class Solution {
public int integerBreak(int n) {
int[] dp = new int[n + 1];
for (int i = 1; i <= n; ++i){
dp[i] = i;
for (int j = 1; j < i; ++j){
dp[i] = Math.max(dp[i], dp[j] * dp[i - j]);
}
}
return n == 2 ? 1 : (n == 3) ? 2 : dp[n];
}
}
本文探讨了三个使用深度优先搜索(DFS)和动态规划(DP)解决的编程问题:1020飞地的数量,72编辑距离,以及322零钱兑换。在飞地问题中,通过DFS遍历矩阵计算无法离开边界的陆地区域;编辑距离问题利用DP计算将一个字符串转换成另一个所需的最少操作数;零钱兑换问题通过DP找到组成给定金额所需的最少硬币数量。这些问题展示了DFS和DP在解决复杂计算问题中的效率和实用性。

1万+

被折叠的 条评论
为什么被折叠?



