**
一.
**美好的八月第一天,从碰到每日一题结束。
不会,复制粘贴交上去的。
要用堆,滑动窗口什么的。一点都不会,留着以后再说吧
只能剽窃我的儿子孙教授的解题思路
首先,我们想一下这样一个问题:题目要求是寻找最小区间,最小区间满足下面两点:
长度最小(首要)
长度相同时起点最小
长度最小的区间必然是需要我们在计算中找到的,但是起点最小的区间我们是可以知道的。就是从每个区间中找最小的元素,组成的新的区间,我们称其为起始区间。如果之后没有长度比起始区间长度更短的,那么起始区间就是我们所求的最小区间,因为起始区间的起点是最小的。
那么我们所需要做的就是搜索是否有比起始区间还要短的区间了。那么该如何搜索呢?
答案是每次都将当前区间中最小的元素丢弃,换成其原始数组中的下一个元素。就是说,如果当前我们从每个区间中选取的元素分别是a1_1,a2_1,a3_1…ak_1(前面的数字代表来自第几个数组,后面的数字表示该数是该数组的第几个元素),若此时最小的元素是ai_1,最大元素是aj_1,那么区间就为[ai_1, aj_1],区间中最小元素是ai_1,那么我们就将ai_1丢弃,将ai_2拿出来放进去。
原因:如果不换ai_1,那么区间的起点就一直是ai_1,而且区间长度不可能缩小,区间长度取决于起点和终点,而终点是不可能变小的。因为我们是从小到大进行元素的选取,我们每次丢弃一个元素,就要选择它在原数组中的下一位,而原数组是升序排列的。故终点不可能减小。
那么,我们只能够通过让起点增大来是区间缩小,即每次丢弃最小的元素,换成它原数组的下一个元素。然后再计算当前所选区间的长度,如果小于之前的区间长度就更新即可。
结束条件:当当前最小元素是其原来整数数组的最后一个元素时,就是结束的时候。因为之后的操作不可能更改起点了,只会让终点变大,即区间变长。
class Solution {
class NumGroup{
public NumGroup(int num, int grp){
this.num = num;
this.grp = grp;
}
int num; //数值
int grp; //组号
}
public int[] smallestRange(List<List<Integer>> nums) {
//因为每次都要找最小元素,所以维护一个最小堆比较合适
PriorityQueue<NumGroup> numgrp = new PriorityQueue<>(new Comparator<NumGroup>(){
@Override
public int compare(NumGroup n1, NumGroup n2){
return n1.num - n2.num;
}
});
int end = -100001;
//记录每个数组当前的指针位置,一开始都指向第0个元素,即每个区间的最小元素
int[] index = new int[nums.size()];
//起始区间
for(int i = 0; i < nums.size(); i++){
if(nums.get(i).get(0) > end) end = nums.get(i).get(0);
NumGroup num = new NumGroup(nums.get(i).get(0), i);
numgrp.offer(num);
}
int max = end;
int start = numgrp.peek().num;
int min = start;
int len = end - start + 1;
while(true){
//grp为当前最小元素的原数组号
int grp = numgrp.poll().grp;
//如果当前最小元素已经是原数组最大元素了,则退出
if(index[grp]+1 == nums.get(grp).size()) break;
//索引++
index[grp]++;
NumGroup n = new NumGroup(nums.get(grp).get(index[grp]), grp);
numgrp.offer(n);
//当前最大值
if(n.num > max) max = n.num;
min = numgrp.peek().num;
//长度变小
if(max-min+1 < len){
start = min;
end = max;
len = max-min+1;
}
}
return new int[]{start, end};
}
}
作者:Girapath
链接:https://leetcode-cn.com/problems/smallest-range-covering-elements-from-k-lists/solution/tan-xin-duo-zhi-zhen-you-xian-ji-dui-lie-by-girapa/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
收获:
关于priorityqueue的应用,碰到了个问题
Cannot infer type arguments for PriorityQueue<>
用了网上的方法改了编译器的参数到5.0以上也没有好,等我以后再研究一下
二.
今天在知乎上看了leecode讲的动态规划,尝试着做了几个简单题。
1692.三步问题。
很简单的动态规划,小孩每次走1或2或3个台阶,问走n个可以有多少种方案。
动态规划首先要分析运用动态规划思想的正确性,
1.问题的优化街=解能否拆成子问题的优化解。
本题中,小孩走n步的优化解,可以由走了n-1再走1,走了n-2再走一次2,走了n-3,再走一次三得到。
**2.**要分析无后效性,n步的优化解fn只与f(n-1)和f(n-2),f(n-3)的值有关,与他们如何得来的无关,也就是说如何得到fn-1,n-2,n-3并不影响到fn。
证明正确性之后就可以构造递归方程了。
收获:
1.这道题思路还是比较清晰的,我这种菜狗都能写几遍就通过。
比较恶心的地方反而是题干中提到的解得数量可能会非常大,要mod1000000007.开始没注意这个,以为求完解得数量之后再mod就行,结果不行,后来发现是因为最终解如果很大,那么最终解前面的那几个子问题也不会小,也是会溢出的,所以这个就得每次求无论什么字问题的解,都得mod一下。
2要注意尽可能减少分支判断的数量和经过数量,我的大概44ms,别人的有的20多ms,我看了一下发现主要是他们想办法精简了if分支数量。
64.最小路径和
一样的很简单地dp
官方的答案给的是要单独建立个存储子问题优化解的矩阵
但是由于每个节点只遍历一次,存储节点值的矩阵其实就可以用来存储dp(也就是到每个节点的最小值)
问题:我的第一次写的太粗糙了。没有考虑边界情况
题解这个写得就很巧妙,不是一次性的把gridij确定下来,而是先整个很大的整数min,先用min横着与grid比较,再竖着比较。就可以很简单地处理边界环境。但是略慢
下面是更快的,但是更占空间,我觉得也没快到哪去,这个本身用的时间就不是太多
class Solution {
public int minPathSum(int[][] grid) {
if(grid == null || grid.length == 0){
return 0;
}
int rows = grid.length;
int cols = grid[0].length;
int[][] dp = new int[rows][cols];
dp[0][0] = grid[0][0];
for(int i = 1; i < rows; i++){
dp[i][0] = dp[i-1][0] + grid[i][0];
}
for(int i = 1; i < cols; i++){
dp[0][i] = dp[0][i-1] + grid[0][i];
}
for(int i = 1; i < rows; i++){
for(int j = 1; j < cols; j++){
dp[i][j] = Math.min(dp[i-1][j], dp[i][j-1])+grid[i][j];
}
}
return dp[rows-1][cols-1];
}
}