小记
本篇博客记录小黑第八次参加leetcode周赛(299场次)的情况,以及对题目的总结,以便鞭策自己不断前进 。
这一次AC三道题,是参加周赛以来第一次AC三道题,虽然第三道题提交的时候正好12:00,没有算到成绩中,但也是自己独立思考完成的,还是值得记录的!!!
题目一:6101. 判断矩阵是否是一个 X 矩阵
思路: 这道题为简单题。直接用暴力解法的思路进行解决。
1,遍历大小为 n ∗ n n*n n∗n矩阵,并检查矩阵的 i i i行的第 i i i个和第 n − 1 − i n-1-i n−1−i个元素是否不为0,如果为0,则返回false。
2,检查矩阵中除步骤1中的其他元素,如果不为0,则返回false。
解决代码:
package leetcode_week_competition.week299;
public class que1 {
public static void main(String[] args) {
int[][] grids ={{5,7,0},{0,3,1},{0,5,0}};
boolean b = checkXMatrix(grids);
System.out.println(b);
}
public static boolean checkXMatrix(int[][] grid) {
int n = grid.length;
for (int i = 0; i <n ; i++) {
if (grid[i][i]==0||grid[i][n-1-i]==0){
return false;
}
for (int j = 0; j <n ; j++) {
if (j!=i &&j!=n-1-i){
if (grid[i][j]!=0){
return false;
}
}
}
}
return true;
}
}
题目二:6100. 统计放置房子的方式数
思路:
注意点:1,街道一侧的房子放置不会影响另一侧的放置,因此只讨论一侧的情况就可了。
2,因为第i块地房子的放置与否与第i块地有关,因此可用动态规划的思路进行解决。
动态规划五部曲:
1,确定dp数组(dp table)以及下标的含义。
d p [ i ] [ j ] dp[i][j] dp[i][j]大小为(n)*(2), d p [ i ] [ 0 ] dp[i][0] dp[i][0]表示第i块地不放房子的方案数, d p [ i ] [ 1 ] dp[i][1] dp[i][1]表示第i块地放房子的方案数。
2,确定递推公式。
d p [ i ] [ 0 ] dp[i][0] dp[i][0] :当第i块地不放房子时,第i-1快地可以放也可以不放。所以: d p [ i ] [ 0 ] = ( d p [ i − 1 ] [ 0 ] + d p [ i − 1 ] [ 1 ] ) % x ; dp[i][0] = (dp[i-1][0]+dp[i-1][1])\%x; dp[i][0]=(dp[i−1][0]+dp[i−1][1])%x;
d p [ i ] [ 1 ] dp[i][1] dp[i][1] :当第i块地放房子时,第i-1块地只能不放。所以 d p [ i ] [ 1 ] = d p [ i − 1 ] [ 0 ] ; dp[i][1] = dp[i-1][0]; dp[i][1]=dp[i−1][0];
3,dp数组初始化
当只有一块地时,只有放与不放两种方案。因此: d p [ 0 ] [ 0 ] = 1 , d p [ 0 ] [ 1 ] = 1 ; dp[0][0] = 1,dp[0][1] = 1; dp[0][0]=1,dp[0][1]=1;
4,确定遍历顺序。
遍历顺序就是按照地的顺序进行。
5,举例推导。(见代码)
最后,一侧的房子放置方案数= d p [ n − 1 ] [ 0 ] + d p [ n − 1 ] [ 1 ] dp[n-1][0]+dp[n-1][1] dp[n−1][0]+dp[n−1][1],总的方案数即为一侧方案数取平方。
解决代码:
package leetcode_week_competition.week299;
public class que2 {
public static void main(String[] args) {
int n=1;
int i = countHousePlacements(1000);
System.out.println(i);
}
public static int countHousePlacements(int n) {
int x= 1000000000+7;
//表示街道一侧第i第放与不放的方式数,0表示不妨,1表示放
long[][] dp =new long[n][2];
dp[0][0] = 1;
dp[0][1] = 1;
for (int i = 1; i < n; i++) {
dp[i][0] = (dp[i-1][0]%x+dp[i-1][1]%x)%x;
dp[i][1] = dp[i-1][0]%x;
}
System.out.println(dp[n-1][0]%x+"======="+dp[n-1][1]%x);
return (int)((((dp[n-1][0]+dp[n-1][1])%x)*((dp[n-1][0]+dp[n-1][1])%x))%x);
}
}
题目三:5229. 拼接数组的最大分数
思路: 本题重点需要认真读题,理清题目需要求得的东西。
问题转化: 题目的要求就是交换两个数组中连续位置的元素,以使得任意一个数组的元素和最大。返回这个最大值。
步骤:
1,首先,数组chazi保存数组 n u m s 1 − n u m s 2 nums1-nums2 nums1−nums2的差值,并求出数组 n u m s 1 nums1 nums1的元素和 s u m 1 sum1 sum1, n u m s 2 nums2 nums2的元素和 s u m 2 sum2 sum2。
2,问题转化为:求数组中连续子数组的最大和最小和。
3,连续子数组的最大和表示,将 n u m s 1 nums1 nums1中这个连续子数组交换到 n u m s 2 nums2 nums2后, s u m 2 sum2 sum2最多能变大为 s u m 2 + m a x A n s sum2+maxAns sum2+maxAns。
连续子数组的最小和的绝对值表示,将
n
u
m
s
2
nums2
nums2中这个连续子数组交换到
n
u
m
s
1
nums1
nums1后,
s
u
m
1
sum1
sum1最多能变大为
s
u
m
1
−
m
i
n
A
n
s
sum1-minAns
sum1−minAns。
4,交换后只有两种情况,要么保证
s
u
m
1
sum1
sum1最大对应步骤3中连续子数组最小和;要么保证
s
u
m
2
sum2
sum2最大对应步骤3中连续子数组最大和。因此结果返回这两种情况中的最大值
解决代码:
package leetcode_week_competition.week299;
public class que3 {
public static void main(String[] args) {
int[] nums1 ={60,60,60};
int[] nums2 ={10,90,10};
int i = maximumsSplicedArray(nums1, nums2);
System.out.println(i);
}
public static int maximumsSplicedArray(int[] nums1, int[] nums2) {
int[] chazhi =new int[nums1.length];
int sum1=0,sum2=0;
for (int i = 0; i <nums1.length ; i++) {
chazhi[i] = nums1[i]-nums2[i];
sum1+=nums1[i];
sum2+=nums2[i];
}
int pre = 0, pree=0,maxAns =chazhi[0],minAns=Integer.MAX_VALUE;
for (int cha:chazhi
) {
pre = Math.max(pre+cha,cha);
pree=Math.min(pree+cha,cha);
maxAns=Math.max(maxAns,pre);
minAns=Math.min(minAns,pree);
}
return Math.max(sum1-minAns,sum2+maxAns);
}
}
提交时间就差一分钟就AC三道了。
题目四:5254. 卖木头块
思路: 本题是一道困难题目。考查DFS的用法。参考大神灵茶山艾府的解题思路,详细步骤可查看[此页面](DFS 时间戳——处理树上问题的有力工具(Python/Java/C++/Go) - 从树中删除边的最小分数 - 力扣(LeetCode))。
关键点: 在 DFS 一棵树的过程中,维护一个全局的时间戳 c l o c k clock clock,每访问一个新的节点,就将 c l o c k clock clock 加一。同时,记录进入节点x 时的时间戳 i n [ x ] in[x] in[x],和离开(递归结束)这个节点时的时间戳 o u t [ x ] out[x] out[x]。
时间戳的性质:
解决代码:
class Solution {
List<Integer>[] g;
int[] nums, xor, in, out;
int clock;
public int minimumScore(int[] nums, int[][] edges) {
var n = nums.length;
g = new ArrayList[n];
for (var i = 0; i < n; i++) g[i] = new ArrayList<>();
for (var e : edges) {
int x = e[0], y = e[1];
g[x].add(y);
g[y].add(x);
}
this.nums = nums;
xor = new int[n];
in = new int[n];
out = new int[n];
dfs(0, -1);
for (var e : edges)
if (!isParent(e[0], e[1])) {
var tmp = e[0];
e[0] = e[1];
e[1] = tmp; // swap,保证 e[0] 是 e[1] 的父节点
}
var ans = Integer.MAX_VALUE;
for (var i = 0; i < edges.length; ++i) {
int x1 = edges[i][0], y1 = edges[i][1];
for (var j = 0; j < i; ++j) {
int x2 = edges[j][0], y2 = edges[j][1];
int x, y, z;
if (isParent(y1, x2)) { // y1 是 x2 的祖先节点(或重合)
x = xor[y2];
y = xor[y1] ^ x;
z = xor[0] ^ xor[y1];
} else if (isParent(y2, x1)) { // y2 是 x1 的祖先节点(或重合)
x = xor[y1];
y = xor[y2] ^ x;
z = xor[0] ^ xor[y2];
} else { // 删除的两条边分别属于两颗不相交的子树
x = xor[y1];
y = xor[y2];
z = xor[0] ^ x ^ y;
}
ans = Math.min(ans, Math.max(Math.max(x, y), z) - Math.min(Math.min(x, y), z));
}
}
return ans;
}
void dfs(int x, int fa) {
in[x] = ++clock;
xor[x] = nums[x];
for (var y : g[x])
if (y != fa) {
dfs(y, x);
xor[x] ^= xor[y];
}
out[x] = clock;
}
boolean isParent(int x, int y) {
return in[x] <= in[y] && in[y] <= out[x];
}
}
总结:以上就是LeetCode第299场周赛题目,这次可以认为是AC了三道题,是第一次一个半小时内做出三道题,表现很好,接下来还是要继续加油!!!争取稳三争四。