第 59 场力扣夜喵双周赛

两道400多五百,后两道都写出代码来了,但是都有问题,哭辽…
还有刚开始第一道测试好慢,搞心态了

5834. 使用特殊打字机键入单词的最少时间

有一个特殊打字机,它由一个 圆盘 和一个 指针 组成, 圆盘上标有小写英文字母 ‘a’ 到 ‘z’。只有 当指针指向某个字母时,它才能被键入。指针 初始时 指向字符 ‘a’ 。

每一秒钟,你可以执行以下操作之一:

将指针 顺时针 或者 逆时针 移动一个字符。
键入指针 当前 指向的字符。
给你一个字符串 word ,请你返回键入 word 所表示单词的 最少 秒数 。

示例 1:

输入:word = “abc”
输出:5
解释:
单词按如下操作键入:
-花 1 秒键入字符 ‘a’ in 1 ,因为指针初始指向 ‘a’ ,故不需移动指针。
-花 1 秒将指针顺时针移到 ‘b’ 。
-花 1 秒键入字符 ‘b’ 。
-花 1 秒将指针顺时针移到 ‘c’ 。
-花 1 秒键入字符 ‘c’ 。

示例 2:

输入:word = “bza”
输出:7
解释:
单词按如下操作键入:
-花 1 秒将指针顺时针移到 ‘b’ 。
-花 1 秒键入字符 ‘b’ 。
-花 2 秒将指针逆时针移到 ‘z’ 。
-花 1 秒键入字符 ‘z’ 。
-花 1 秒将指针顺时针移到 ‘a’ 。
-花 1 秒键入字符 ‘a’ 。

示例 3:

输入:word = “zjpc”
输出:34
解释:
单词按如下操作键入:
-花 1 秒将指针逆时针移到 ‘z’ 。
-花 1 秒键入字符 ‘z’ 。
-花 10 秒将指针顺时针移到 ‘j’ 。
-花 1 秒键入字符 ‘j’ 。
-花 6 秒将指针顺时针移到 ‘p’ 。
-花 1 秒键入字符 ‘p’ 。
-花 13 秒将指针逆时针移到 ‘c’ 。
-花 1 秒键入字符 ‘c’ 。

提示:

1 <= word.length <= 100
word 只包含小写英文字母。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/minimum-time-to-type-word-using-special-typewriter
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

题目描述

就是转动呗,比较顺时针还是逆时针转步数少,想像时钟,12个数,0到6,半数是分界线

class Solution {public int minTimeToType(String word) {int l = word.length();int count = l;char c = 'a';for(int i = 0; i < l; i++){char temp = word.charAt(i);if(Math.abs(temp - c) < 14)count += Math.abs(temp - c);else{count += 26 - Math.abs(temp - c);}c = temp;}return count;}
}

5835. 最大方阵和

题目描述

给你一个 n x n 的整数方阵 matrix 。你可以执行以下操作 任意次 :

选择 matrix 中 相邻 两个元素,并将它们都 乘以 -1 。
如果两个元素有 公共边 ,那么它们就是 相邻 的。

你的目的是 最大化 方阵元素的和。请你在执行以上操作之后,返回方阵的 最大 和。

示例 1:

输入:matrix = [[1,-1],[-1,1]]
输出:4
解释:我们可以执行以下操作使和等于 4 :
-将第一行的 2 个元素乘以 -1 。
-将第一列的 2 个元素乘以 -1 。

示例 2:

输入:matrix = [[1,2,3],[-1,-2,-3],[1,2,3]]
输出:16
解释:我们可以执行以下操作使和等于 16 :
-将第二行的最后 2 个元素乘以 -1 。

提示:

n == matrix.length == matrix[i].length
2 <= n <= 250
-10^5 <= matrix[i][j] <= 10^5

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/maximum-matrix-sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

因为第二题,肯定不会让一个个交换的
所以,还是贪心
可以发下,两个相邻数字都乘以-1,如果两个数字都是负数,那么就会变成都是正数,我们期望都是这样的;如果一个正,一个负,那么就可以将负数移动位置;
通过这个规律,如果有偶数个负数,那么通过交换,都可以将负数两两配对,然后最大和就是所有数的绝对值
如果有奇数个负数,那么通过交换,可以使负数只剩下一个,并且可以使这个负数是绝对值最小的数,所以最大和就是整个矩阵的绝对值和,减去这个负数绝对值×2

class Solution {public long maxMatrixSum(int[][] matrix) {//想想int min = Integer.MAX_VALUE;int n = matrix.length;long sum = 0;int count = 0;for(int i = 0; i < n; i++){for(int j = 0; j < n; j++){count += matrix[i][j] > 0 ? 0 : 1;min = Math.min(matrix[i][j] > 0 ? matrix[i][j] : -matrix[i][j], min);sum += matrix[i][j] > 0 ? matrix[i][j] : -matrix[i][j];}}return count % 2 == 0? sum : sum - min - min;}
}

5836. 到达目的地的方案数

题目描述

你在一个城市里,城市由 n 个路口组成,路口编号为 0 到 n - 1 ,某些路口之间有 双向 道路。输入保证你可以从任意路口出发到达其他任意路口,且任意两个路口之间最多有一条路。

给你一个整数 n 和二维整数数组 roads ,其中 roads[i] = [ui, vi, timei] 表示在路口 ui 和 vi 之间有一条需要花费 timei 时间才能通过的道路。你想知道花费 最少时间 从路口 0 出发到达路口 n - 1 的方案数。

请返回花费 最少时间 到达目的地的 路径数目 。由于答案可能很大,将结果对 109 + 7 取余 后返回。

示例 1:

输入:n = 7, roads = [[0,6,7],[0,1,2],[1,2,3],[1,3,3],[6,3,3],[3,5,1],[6,5,1],[2,5,1],[0,4,5],[4,6,2]]
输出:4
解释:从路口 0 出发到路口 6 花费的最少时间是 7 分钟。
四条花费 7 分钟的路径分别为:
- 0 ➝ 6
- 0 ➝ 4 ➝ 6
- 0 ➝ 1 ➝ 2 ➝ 5 ➝ 6
- 0 ➝ 1 ➝ 3 ➝ 5 ➝ 6

示例 2:

输入:n = 2, roads = [[1,0,10]]
输出:1
解释:只有一条从路口 0 到路口 1 的路,花费 10 分钟。

提示:

1 <= n <= 200
n - 1 <= roads.length <= n * (n - 1) / 2
roads[i].length == 3
0 <= ui, vi <= n - 1
1 <= timei <= 109
ui != vi
任意两个路口之间至多有一条路。
从任意路口出发,你能够到达其他任意路口。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/number-of-ways-to-arrive-at-destination
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

dfs肯定会超时,但是我想的是先写个dfs,然后记忆化优化一下,结果想了半天也不知道怎么优化
然后也想到了最短路,但是感觉最短路都是求到所有点的路,感觉用不上,就跳了
当时的dfs代码,十几个例子就超时了:

class Solution {long min = 0;int count = 0;Map<Integer, Map<Integer, Integer>> map;int n;boolean[] used;int MOD = (int)1e9 + 7;long[][] memo;public int countPaths(int n, int[][] roads) {//先写个暴力看看行不行吧this.n = n;int l = roads.length;used = new boolean[n];map = new HashMap<>();memo = new long[n][n];   //两点之间花费的时间for(int i = 0; i < n; i++){for(int j = 0; j < n; j++){memo[i][j] = Long.MAX_VALUE;}}for(int i = 0; i < l; i++){int[] temp = roads[i];Map<Integer, Integer> in = map.getOrDefault(temp[0], new HashMap<>());in.put(temp[1], temp[2]);map.put(temp[0], in);Map<Integer, Integer> in2 = map.getOrDefault(temp[1], new HashMap<>());in2.put(temp[0], temp[2]);map.put(temp[1], in2);}dfs(0, 0);return count;}public void dfs(int k, long time){if(k == n - 1){if(min == 0 || time < min){min = time;count = 1;}else if(time == min){count += 1;}return;}Map<Integer, Integer> temp = map.get(k);for(int r : temp.keySet()){if(used[r])continue;if(min != 0 && time + temp.get(r) > min)continue;used[r] = true;dfs(r, time + temp.get(r));used[r] = false;}}
}

不会做,想不到咋做?
看了官解,然后又看了讨论里写的答案,仔细思考了一下
说到底,是先求最短路,再统计最短路的数量
而迪杰斯特拉本来就是求到每个节点最短路的算法,而在到达最短路的同时,也可以用一个数组记录到达每个节点最短路的数量大小

先写了个这个代码,然后第45个例子报错了,原因是因为超整数范围了

class Solution {public static final int MOD = (int)1e9 + 7;public int countPaths(int n, int[][] roads) {//先用迪杰斯特拉处理,计算出到每个点的最短路//然后再用最短路创建一个有向无环图,然后再图上dfs找到达n-1最短路的数目//但是迪杰斯特拉求最短路的时候,就可以用一个数组记录达到每个点最短路的数目//首先还是要预处理每两个点之间的距离int l = roads.length;if(n == 1)return 1;Map<Integer, Map<Integer, Integer>> map = new HashMap<>();for(int i = 0; i < l; i++){int[] temp = roads[i];Map<Integer, Integer> in = map.getOrDefault(temp[0], new HashMap<>());in.put(temp[1], temp[2]);map.put(temp[0], in);Map<Integer, Integer> in2 = map.getOrDefault(temp[1], new HashMap<>());in2.put(temp[0], temp[2]);map.put(temp[1], in2);}int[] dist = new int[n];    //到达每个点的最小路径距离Arrays.fill(dist, Integer.MAX_VALUE);int[] count = new int[n];   //每个点最短路的数目//从0到0路径一条count[0] = 1;//迪杰斯特拉算法//存放点和距离PriorityQueue<int[]> pq = new PriorityQueue<>((a,b) -> (a[1] == b[1] ? a[0] - b[0] : a[1] - b[1]));//初始,从0点到达0的距离是0pq.offer(new int[]{0, 0});while(!pq.isEmpty()){int[] top = pq.poll();int p = top[0];int d = top[1];//System.out.print(p + " ");//System.out.println(d);//如果超过了最短路径,那么就跳过if(d > dist[p])continue;//取出与p相连的节点,把它们都加到优先队列中,距离要累计Map<Integer, Integer> rlt = map.get(p);for(int key : rlt.keySet()){//如果距离大于最短距离,跳过;if(rlt.get(key) + d > dist[key])continue;//如果等于最短路,那么通过这条路到达最短路的路径数就是count[p]if(rlt.get(key) + d == dist[key]){count[key] = (count[key] + count[p]) % MOD;}else{//如果小于最短路,那么就加入优先队列pq.offer(new int[]{key, rlt.get(key) + d});//更新最短路,同时更新count数组dist[key] = rlt.get(key) + d;count[key] = (count[key] + count[p]) % MOD;;}}}return count[n - 1];}
}

然后我就想都改成long就完事了呗,然后:

PriorityQueue<long[]> pq = new PriorityQueue<>((a,b) -> (a[1] == b[1] ? a[0] - b[0] : a[1] - b[1]));

这样写,一直出错,放到idea里一看,原因是因为,compare函数,也就是默认用的比较函数的返回值是int,这样写的话,相减结果是long,所以要转换成int
也就是这样:

PriorityQueue<long[]> pq = new PriorityQueue<>((a,b) -> (int)(a[1] == b[1] ? a[0] - b[0] : a[1] - b[1]));

以为这样就可以了,结果一提交,变成过了44个,醉了,我是不知道咋改了

然后看到个这样的代码,全部用的int,还能过,咱也不知道为啥了,就这样吧,思路懂了就行了

class Solution {public int countPaths(int n,int[][] times) {final int INF = Integer.MAX_VALUE;List<int[]>[] g = new List[n];for (int i = 0; i < n; ++i) {g[i] = new ArrayList<int[]>();}for (int[] t : times) {int x = t[0] , y = t[1];g[x].add(new int[]{y, t[2]});g[y].add(new int[]{x, t[2]});}int mod=1000000007;int[][] dist = new int[n][2];for (int i = 0; i < n; i++) {dist[i][0] = INF;}dist[0][0] = 0;dist[0][1] = 1;int ans = 0;PriorityQueue<int[]> pq = new PriorityQueue<int[]>((a, b) -> a[0] != b[0] ? a[0] - b[0] : a[1] - b[1]);pq.offer(new int[]{0, 0});while (!pq.isEmpty()) {int[] p = pq.poll();int time = p[0], x = p[1];if (dist[x][0] < time) {continue;}for (int[] e : g[x]) {int y = e[0], d = dist[x][0] + e[1];if (d == dist[y][0]) {dist[y][1]+=dist[x][1];dist[y][1]%=mod;}if (d < dist[y][0]) {dist[y][0] = d;pq.offer(new int[]{d, y});dist[y][1]=dist[x][1];}}}return  dist[n-1][1];}
}作者:lowry-4
链接:https://leetcode-cn.com/problems/number-of-ways-to-arrive-at-destination/solution/java-dijkstrasuan-fa-by-lowry-4-vrdw/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

5837. 划分数字的方案数

题目描述

你写下了若干 正整数 ,并将它们连接成了一个字符串 num 。但是你忘记给这些数字之间加逗号了。你只记得这一列数字是 非递减 的且 没有 任何数字有前导 0 。

请你返回有多少种可能的 正整数数组 可以得到字符串 num 。由于答案可能很大,将结果对 109 + 7 取余 后返回。

示例 1:

输入:num = “327”
输出:2
解释:以下为可能的方案:
3, 27
327

示例 2:

输入:num = “094”
输出:0
解释:不能有数字有前导 0 ,且所有数字均为正数。

示例 3:

输入:num = “0”
输出:0
解释:不能有数字有前导 0 ,且所有数字均为正数。

示例 4:

输入:num = “9999999999999”
输出:101

提示:

1 <= num.length <= 3500
num 只含有数字 ‘0’ 到 ‘9’ 。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/number-of-ways-to-separate-numbers
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

我是这样想的,先预处理每两个位置间是否能形成一个非递减并且没有前导0的数
然后动规,处理以每个位置为结尾的情况,我觉得思路没错,但老是报错
下面是当时写的代码:

class Solution {int MOD = (int)1e9 + 7;public int numberOfCombinations(String num) {int l = num.length();int[][] dp = new int[l][l];for(int i = 0; i < l; i++){if(num.charAt(i) == '0'){for(int j = i + 1; j < l; j++){dp[i][j] = -1;}continue;}dp[i][i] = 1;}for(int i = 0; i < l; i++){for(int j = i + 1; j < l; j++){if(dp[i][j] != 0)continue;if(j - i > 1){dp[i][j] = dp[i][j - 1];if(num.charAt(j) >= num.charAt(j - 1))dp[i][j] = 1;}else{dp[i][j] = num.charAt(j) >= num.charAt(j - 1) ? 1 : -1;}System.out.print(dp[i][j] + " ");}System.out.println();}int[] f = new int[l];f[0] = dp[0][0] == 1 ? 1 : 0;for(int i = 1; i < l; i++){if(dp[0][i] == 1)f[i] += 1;for(int j = 0; j < i; j++){if(dp[j + 1][i] == 1){f[i] = (f[i] + f[j]) % MOD;}}}return f[l - 1];}
}

我理解错了,非递减的意思是前一个数字和后一个数字是非递减的。。。不是每个数字中的数是非递减的

看了半天,看不太进去了,心有点乱,大致思路明白了:
挺难的,确实不好想
首先dp[i][j]表示到i位置,并且最后一个数字的长度为j时的方案数。那么也就是说,可以从dp[i - j][k]转移而来,这里k要小于等于j。k小于j很好理解,因为长度小的数字,肯定小于长度大的数字;还有一种特殊的,就是k和j相等的时候。这样动态规划,需要枚举i,j,k,时间复杂度是3次方
当k < j的时候,我们本来是需要枚举k,然后来计算所有k的dp[i-j][k]的和,但是的,可以发现,这些和是连续的,所以可以用前缀和来进行处理
而当k == j的时候,还是要比较两个数的大小,在最坏的情况下还是O(n)的,所以还要优化;而优化又是通过一次动态规划,令c[i][j]c[i][j]表示第一个串从ii开始,第二个串从jj开始,且满足第一个串不大于第二个串的最长长度。显然c[i][j]c[i][j]可以由c[i+1][j+1]c[i+1][j+1]转移得到。
以后有空再看了!
lcp 来源于Longest Common Prefix,即最长公共前缀。

第255场周赛

300多四百

5850. 找出数组的最大公约数

题目描述

给你一个整数数组 nums ,返回数组中最大数和最小数的 最大公约数 。

两个数的 最大公约数 是能够被两个数整除的最大正整数。

示例 1:

输入:nums = [2,5,6,9,10]
输出:2
解释:
nums 中最小的数是 2
nums 中最大的数是 10
2 和 10 的最大公约数是 2

示例 2:

输入:nums = [7,5,6,8,3]
输出:1
解释:
nums 中最小的数是 3
nums 中最大的数是 8
3 和 8 的最大公约数是 1

示例 3:

输入:nums = [3,3]
输出:3
解释:
nums 中最小的数是 3
nums 中最大的数是 3
3 和 3 的最大公约数是 3

提示:

2 <= nums.length <= 1000
1 <= nums[i] <= 1000

思路

找最大数最小数,一看范围很小,直接挨个遍历找最大公因数

class Solution {public int findGCD(int[] nums) {int min = 5000;int max = 0;for(int n : nums){min = Math.min(min, n);max = Math.max(max, n);}for(int i = min; i >= 1; i--){if(max % i == 0 && min % i == 0)return i;}return 1;}
}

5851. 找出不同的二进制字符串

题目描述

给你一个字符串数组 nums ,该数组由 n 个 互不相同 的二进制字符串组成,且每个字符串长度都是 n 。请你找出并返回一个长度为 n 且 没有出现 在 nums 中的二进制字符串。如果存在多种答案,只需返回 任意一个 即可。

示例 1:

输入:nums = [“01”,“10”]
输出:“11”
解释:“11” 没有出现在 nums 中。“00” 也是正确答案。

示例 2:

输入:nums = [“00”,“01”]
输出:“11”
解释:“11” 没有出现在 nums 中。“10” 也是正确答案。

示例 3:

输入:nums = [“111”,“011”,“001”]
输出:“101”
解释:“101” 没有出现在 nums 中。“000”、“010”、“100”、“110” 也是正确答案。

提示:

n == nums.length
1 <= n <= 16
nums[i].length == n
nums[i] 为 ‘0’ 或 ‘1’

思路

就是求二进制数呗,估计能用API,但是我没想到用哪个,就自己写了
先把给的二进制数放在set中,然后从0到1<<n,找哪个没在set中出现的
然后把这个数转换成二进制数的形式,注意补零

class Solution {public String findDifferentBinaryString(String[] nums) {int l = nums.length;Set<Integer> set = new HashSet<>();for(int i = 0; i < l; i++){String s = nums[i];int n = 0;for(int j = 0; j < s.length(); j++){n = n * 2 + s.charAt(j) - '0';}set.add(n);}int t = 0;for(int mask = 0; mask < (1 << l); mask++){if(!set.contains(mask)){t = mask;break;}}StringBuffer sb = new StringBuffer();if(t == 0){for(int i = 0; i < l; i++){sb.append("0");}}else{while(t != 0){sb.append(t % 2);t /= 2;}while(sb.length() < l){sb.append("0");}}sb.reverse();return sb.toString();}
}

看了个简单的想法,就是对于每一位,都取一个和nums中元素不相同的值,然后拼接起来就行了

class Solution {public String findDifferentBinaryString(String[] nums) {int l = nums.length;StringBuffer sb = new StringBuffer();for(int i = 0; i < l; i++){sb.append(nums[i].charAt(i) == '0' ? "1" : "0");}return sb.toString();}
}

借助直接转换成二进制的API

class Solution
{public String findDifferentBinaryString(String[] nums) {int n = nums.length;Set<Integer> us = new HashSet<>();for (String x : nums)//直接转换成二进制us.add(Integer.parseInt(x, 2));int x = 0;//找到集合中没有的while (us.contains(x) == true)x++;//然后转换成二进制String res = Integer.toBinaryString(x);if (res.length() == n)return res;//补0StringBuilder sb = new StringBuilder();int prefix_len = n - res.length();while (prefix_len > 0){sb.append('0');prefix_len --;}String prefix = new String(sb);return prefix + res;        }
}作者:Hanxin_Hanxin
链接:https://leetcode-cn.com/problems/find-unique-binary-string/solution/cpython3java-1jie-zhu-wu-xu-ji-he-by-han-vvol/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

5852. 最小化目标值与所选元素的差

题目描述

给你一个大小为 m x n 的整数矩阵 mat 和一个整数 target 。

从矩阵的 每一行 中选择一个整数,你的目标是 最小化 所有选中元素之 和 与目标值 target 的 绝对差 。

返回 最小的绝对差 。

a 和 b 两数字的 绝对差 是 a - b 的绝对值。

示例 1:

输入:mat = [[1,2,3],[4,5,6],[7,8,9]], target = 13
输出:0
解释:一种可能的最优选择方案是:
- 第一行选出 1
- 第二行选出 5
- 第三行选出 7
所选元素的和是 13 ,等于目标值,所以绝对差是 0 。

示例 2:

输入:mat = [[1],[2],[3]], target = 100
输出:94
解释:唯一一种选择方案是:
- 第一行选出 1
- 第二行选出 2
- 第三行选出 3
所选元素的和是 6 ,绝对差是 94 。

示例 3:

输入:mat = [[1,2,9,8,7]], target = 6
输出:1
解释:最优的选择方案是选出第一行的 7 。
绝对差是 1 。

提示:

m == mat.length
n == mat[i].length
1 <= m, n <= 70
1 <= mat[i][j] <= 70
1 <= target <= 800

思路

我刚开是一看,就以为是升级版的两数之和
然后就想到了之前同学说的给定四个list,求和为target的四个数
然后就想到了分治的思想,先将m行一直往下分,分成两行两行的,然后把两行和的所有情况放在一个数组中,然后继续向上和,最后就得到了所有行和的情况
这个其实和暴力没什么区别,暴力也是计算所有行和的情况,但是分治时间复杂度是对数级别,
本来觉得这样写估计也过不了,但是看了第四题实在不会,这个又想不到更好的,所以也就这样写了,没想到过了

class Solution {public int minimizeTheDifference(int[][] mat, int target) {int m = mat.length;int n = mat[0].length; int[] res = dfs(mat, 0, m - 1);Arrays.sort(res);int idx = -1;for(int i = 0; i < res.length; i++){if(res[i] > target){idx = i;break;}}if(idx == 0)return res[0] - target;else if(idx == -1)return target - res[res.length - 1];elsereturn target - res[idx - 1] > res[idx] - target ? res[idx] - target : target - res[idx - 1];}//i到j行的和的情况public int[] dfs(int[][] mat, int i, int j){if(i > j)return new int[]{};if(j == i)return mat[i];int mid = i + (j - i) / 2;int[] xiao = dfs(mat, i, mid);int[] da = dfs(mat, mid + 1, j);Set<Integer> set = new HashSet<>();for(int x : xiao){for(int y : da){set.add(x + y);}}int[] res = new int[set.size()];int idx = 0;for(int x : set){res[idx++] = x;}return res;}
}

我这个是过了,但是如果说去计算时间复杂度的话,应该在10^7左右
然后看了一下主流的解法,分组背包
其实就是再加一层遍历,遍历每个组内的所有物件,第一次见

class Solution {public int minimizeTheDifference(int[][] mat, int target) {//没想到是背包问题,听说叫分组背包int m = mat.length;int n = mat[0].length;//dp[i][j]定义为i行,和为j的情况boolean[][] dp = new boolean[m + 1][5000];dp[0][0] = true;for(int i = 1; i <= m; i++){for(int j = 0; j < 5000; j++){//遍历mat这一行的所有数,for(int k = 0; k < n; k++){if(j >= mat[i - 1][k]){dp[i][j] = dp[i][j] | dp[i - 1][j - mat[i - 1][k]];//如果当前值为true,就跳出,因为没必要遍历后面的数了if(dp[i][j])break;}}}}//最后找和target相近的数int res = 5000;for(int i = 0; i < 5000; i++){if(dp[m][i])res = Math.min(Math.abs(target - i), res);}return res;}
}

优化一下,因为5000太大了,target最大才800
所以开一个target的数组表示情况就可以了,但是由于可能会出现大于target的最小数来构成差的最小值,所以还要用一个额外的变量来记录这个值,细节比较多:

优化过的一维dp,1800ms直接降成了100ms:

class Solution {public int minimizeTheDifference(int[][] mat, int target) {//没想到是背包问题,听说叫分组背包//再写一个用target优化的,一维的int m = mat.length;int n = mat[0].length;//dp[i][j]定义为i行,和为j的情况boolean[] dp = new boolean[target];dp[0] = true;//记录每一行大于target的最小值与target之差int large_min = 5000;for(int i = 0; i < m; i++){//先将这一行排序,方便优化Arrays.sort(mat[i]);//用来存储当前行的结果boolean[] f = new boolean[target];int next_large = 5000;for(int j = 0; j < target; j++){//遍历mat这一行的所有数,for(int k = 0; k < n; k++){if(dp[j]){//如果这个数大于target了,就把这个差值赋值给变量,同时跳出循环(因为排序,后面的都比这个值大)if(j + mat[i][k] >= target){next_large = Math.min(next_large, j + mat[i][k] - target);break;}else{f[j + mat[i][k]] = true;}}}}large_min = Math.min(next_large, large_min + mat[i][0]);dp = f;}//最后找和target相近的数int res = 5000;for(int i = target - 1; i >= 0; i--){if(dp[i]){res = Math.min(Math.abs(target - i), res);break;    }}res = Math.min(res, large_min);return res;}
}

5853. 从子集的和还原数组

题目描述

存在一个未知数组需要你进行还原,给你一个整数 n 表示该数组的长度。另给你一个数组 sums ,由未知数组中全部 2n 个 子集的和 组成(子集中的元素没有特定的顺序)。

返回一个长度为 n 的数组 ans 表示还原得到的未知数组。如果存在 多种 答案,只需返回其中 任意一个 。

如果可以由数组 arr 删除部分元素(也可能不删除或全删除)得到数组 sub ,那么数组 sub 就是数组 arr 的一个 子集 。sub 的元素之和就是 arr 的一个 子集的和 。一个空数组的元素之和为 0 。

注意:生成的测试用例将保证至少存在一个正确答案。

示例 1:

输入:n = 3, sums = [-3,-2,-1,0,0,1,2,3]
输出:[1,2,-3]
解释:[1,2,-3] 能够满足给出的子集的和:
- []:和是 0
- [1]:和是 1
- [2]:和是 2
- [1,2]:和是 3
- [-3]:和是 -3
- [1,-3]:和是 -2
- [2,-3]:和是 -1
- [1,2,-3]:和是 0
注意,[1,2,-3] 的任何排列和 [-1,-2,3] 的任何排列都会被视作正确答案。

示例 2:

输入:n = 2, sums = [0,0,0,0]
输出:[0,0]
解释:唯一的正确答案是 [0,0] 。

示例 3:

输入:n = 4, sums = [0,0,5,5,4,-1,4,9,9,-1,4,3,4,8,3,8]
输出:[0,-1,4,5]
解释:[0,-1,4,5] 能够满足给出的子集的和。

提示:

1 <= n <= 15
sums.length == 2^n
-10^4 <= sums[i] <= 10^4

思路

完全没有任何思路,第三道是知道n个数,求和,这个是知道和的情况,求n个数。。。

也看了老半天,看明白如果子集中都是正数的话,该怎么求了。如果是都是正数,那么最小值子集中的最小值,除了0以外,就是数组中的最小值。然后从子集中删去这个值,那么剩下的子集中,最小值就是第二个小的数;取出第二个数,然后再取出已知的两个数的子集,这时候剩下子集中的最小数就是第三个数。。然后再取出当前已知元素的子集,以此类推,就可以得到原数组中的所有元素

如果有负数,是这样操作的,先找出子集中的最小数m(也就是原数组中所有负数的和),然后将子集中所有值加上这个最小数的相反数-m,这时候子集就变成了一个非负数的集合,用刚刚所说的正数集合的方法找到这个集合的数组temp
在temp数组中,找到和为-m的一个子集,然后把这个子集中的数都取相反数,最后得到的整个数组,就是答案

证明看不懂。。这里贴一个别人java写的,以后再看
还有一个种一个个划分的方法,也看了一下,都挺难想的

class Solution {public int[] recoverArray(int n, int[] sums) {int bias = 0;for (int sum : sums) {bias = Math.min(bias, sum);}bias = -bias;TreeMap<Integer, Integer> treeMap = new TreeMap<>();for (int sum : sums) {treeMap.merge(sum + bias, 1, Integer::sum);}int[] ans = new int[n];int first = treeMap.firstKey();treeMap.put(first, treeMap.get(first) - 1);if (treeMap.get(first) == 0) {treeMap.remove(first);}ans[0] = treeMap.firstKey();for (int i = 1; i < n; i++) {for (int mask = 0; mask < (1 << i); mask++) {if (((mask >>> (i - 1)) & 1) != 0) {int sum = 0;for (int j = 0; j < i; j++) {if (((mask >>> j) & 1) != 0) {sum += ans[j];}}treeMap.put(sum, treeMap.get(sum) - 1);if (treeMap.get(sum) == 0) {treeMap.remove(sum);}}}ans[i] = treeMap.firstKey();}for (int i = 0; i < (1 << n); i++) {int sum = 0;for (int j = 0; j < n; j++) {if (((i >>> j) & 1) != 0) {sum += ans[j];}}if (sum == bias) {for (int j = 0; j < n; j++) {if (((i >>> j) & 1) != 0) {ans[j] = -ans[j];}}break;}}return ans;}
}作者:jackie-tien
链接:https://leetcode-cn.com/problems/find-array-given-subset-sums/solution/javaban-ben-by-jackie-tien-e4n4/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

总结

再来总结一下,这两次的贪心题,自己都想到的比较快,然后名次就比较高。周赛的第三题凭借之前有过类似的经验也侥幸过了,最后复查能不能过还是个问题。
对最短路径问题的理解又多了一点,很多问题得先求出最短路径,再进行其他处理
但是这两个第四题真的有点超出我思考范围了,感觉好难想,先放着了
总体来说还算有进步吧,继续加油了

LeetCode 第 59 场力扣夜喵双周赛(最短路径数+迪杰斯特拉、动态规划+最长公共前缀问题) / 第255场周赛(二进制转换,分组背包,子集还原数组(脑筋急转弯))相关推荐

  1. LeetCode 第 58 场力扣夜喵双周赛(动态规划、马拉车算法,前后缀处理)/ 第 253 场力扣周赛(贪心,LIS)

    第 58 场力扣夜喵双周赛 两道600多 5193. 删除字符使字符串变好 题目描述 一个字符串如果没有 三个连续 相同字符,那么它就是一个 好字符串 . 给你一个字符串 s ,请你从 s 删除 最少 ...

  2. LeetCode第 57 场力扣夜喵双周赛(差分数组、单调栈) and 第 251 场力扣周赛(状态压缩动规,树的序列化,树哈希,字典树)

    LeetCode第 57 场力扣夜喵双周赛 离knight勋章越来越近,不过水平没有丝毫涨进 1941. 检查是否所有字符出现次数相同 题目描述 给你一个字符串 s ,如果 s 是一个 好 字符串,请 ...

  3. 第 304 场力扣周赛

    1.Introduction 平常做代码题目较少,今天迟迟不能入睡,我思考了下,确实有很大概率,对十年内做的目标,有很大可能不能实现.于是做了几道题勉强让自己心安.一边喝着牛栏山壮精神,一边写Leet ...

  4. 第 256 场力扣周赛(状态压缩+dp,二进制子序列的动规、940)

    第 256 场力扣周赛 有事没做,来看一下题 5854. 学生分数的最小差值 题目描述 给你一个 下标从 0 开始 的整数数组 nums ,其中 nums[i] 表示第 i 名学生的分数.另给你一个整 ...

  5. 第 254 场力扣周赛(KMP、贪心、快速幂、二分+多源bfs、并查集 + 时光倒流)

    第 254 场力扣周赛 稀里糊涂双眼双眼惺忪的做了三道,错了4次...还是600来名 5843. 作为子字符串出现在单词中的字符串数目 题目描述 给你一个字符串数组 patterns 和一个字符串 w ...

  6. 第 270 场力扣周赛

    第一百一十二天 --- 第 270 场力扣周赛 题目一 思路:直接模拟 细节 代码 附加 题目二 思路 细节 代码 题目一 力扣:2094. 找出 3 位偶数 思路:直接模拟 1.因为构造所有三位数, ...

  7. 第 321 场力扣周赛

    第 321 场力扣周赛 T1 求和 class Solution { public:int pivotInteger(int n) {int sum=(1+n)*n/2;int cnt=0;for(i ...

  8. 力扣第十五题-三数之和

    前言 力扣第十五题 三数之和 如下所示: 给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复 ...

  9. LeetCode 13罗马数字转整数14最长公共前缀

    罗马数字转整数 上一题是整数转罗马数字,这题是罗马数字转整数.虽然是简单题,但我感觉其实有点烦. 上一次是数字转字符,这次是字符转数字,总的来说大体思想还是差不多的. 首先整个字符串可能是这样构造的: ...

最新文章

  1. 那位五十多岁的创业者给我的启示!
  2. 【初探HTML本相】道之真谛不过自然,html标签脱俗还真
  3. mysql中修改字段的类型
  4. 如何从 100 亿 URL 中找出相同的 URL?
  5. 学军中学推理社2017届招新试题
  6. 06_排序_希尔排序
  7. 学习笔记(03):Python实战编程-sys模块
  8. Android风格ppt,Material Design风格的快手PPT
  9. xrdpdf卡片在哪可下载_暑假学习英语字母,就是这样简单(附可打印字母卡下载)...
  10. 程序猿爆笑选集(1)
  11. idea主题颜色Linux,intellij idea 主题大全,看不惯idea 那2种主题的来这里了
  12. 《智能车制作》这本书已经把平衡车的控制原理讲得很清楚了!附上其他书讲平衡车的部分。
  13. 圣思园Java视频 学习笔记
  14. CSUOJ1238--兵临城下
  15. 工业防腐漆的作用,你知道的有几个
  16. tm1650中文资料_TM1650+msp430单片机 调试及遇到问题的总结
  17. 夜神模拟器连接loaclhost
  18. [置顶]       VMWare不能安装64位操作系统原因探析
  19. 武汉本地三大门户网站的分析和比较!
  20. 【艾思软件】微信小程序开发报价方案模版

热门文章

  1. git学习总结02 — 版本控制
  2. Python 数据分析实战案例:基于电商销售数据的 RFM 模型构建
  3. 计算机公式固定数值符号,中级无纸化机考 数学公式/符号你会输入吗?
  4. 小波神经网络的基本原理,小波神经网络辨识分析
  5. 项目整合管理——批准的变更请求
  6. 和亲人相处的时光,就是最美的时光。
  7. 亚马逊云科技赋能各行各业,帮助客户数字化转型
  8. Android开发了解这些自然无惧面试,终局之战
  9. OJ平台输入输出注意事项
  10. WiFi - AP 隔离