题目
lc639. 解码方法 II
lc437. 路径总和III
lc517. 超级洗衣机
lc223. 矩形面积
09-27 lc639. 解码方法 II
-
一道hard题,难点在于思考的全面性,容易漏case
-
我做了挺长时间,因为我把 1 e 9 + 7 1e9+7 1e9+7写成了 10 e 9 + 7 10e9+7 10e9+7,真的服了,大哭,我是伞兵
-
首先这是个dp问题,dp的比较简单
-
按位读,每次进行判断
- 如果是*,则本身能够表示9个数字,然后再看前面的字符是否有
- 如果前面的字符是1 or 2,那么就多一种可能
- 如果前面的字符是*,则就是连续两个*,因为*只能表示1-9,所以有15个可能
- 如果当前是0,
- 那么要看前一位是不是1 or 2 or *,那就是错误case
- 如果当前是普通数字
- 首先自己就是一种可能
- 然后看前一位,分别进行判断,主要要检验合法性
- 如果是*,则本身能够表示9个数字,然后再看前面的字符是否有
-
这一题其实和ip地址那题很像,但是难点在于多了个*,就让问题更麻烦了,不复杂,但麻烦,其实本题意义不大
class Solution {
//解码,和那个ip地址很像
//返回的是数目
//其中还有*,有*的话,可能有多种,则需要按照前面的来看
//是个dp问题
//要分当前是*还是数字,两种情况要分类讨论,挺麻烦的
//还要区分是不是0
public int numDecodings(String s) {
char[] ss = s.toCharArray();
int n = ss.length;
long[] f = new long[n];
for(int i=0; i<n; i++){
if(ss[i]=='*'){
//f[i-1] * 9
f[i] = i>=1 ? 9*f[i-1] : 9;
f[i] %= 10e9 +7;
// f[i-2] * n
if(i>=1){
if(ss[i-1]=='1') f[i] += (i>=2?f[i-2]:1) * 9;
if(ss[i-1]=='2') f[i] += (i>=2?f[i-2]:1) * 6;
if(ss[i-1]=='*') f[i] += (i>=2?f[i-2]:1) * 15; // *代表1-9。所以只能有1 1-9, 2 1-6 这15个
}
}
else if(ss[i]=='0'){
//0只能和其他的一起
if(i==0||ss[i-1]=='0') return 0;
if(ss[i-1]=='*') f[i] += i>=2?2*f[i-2]:2;
else if(ss[i-1]=='1'||ss[i-1]=='2') f[i] += i>=2?f[i-2]:1;
else return 0;
}
else{
f[i] = i>=1?f[i-1]:1;
f[i] %= 10e9 +7;
if(i>=1){
if(ss[i-1]=='1') f[i] += i>=2?f[i-2]:1;
if(ss[i-1]=='2'&&ss[i]-'0'<7) f[i] += i>=2?f[i-2]:1;
if(ss[i-1]=='*'){
if(ss[i]-'0'<7) f[i] += i>=2?2*f[i-2]:2;
else f[i] += i>=2?f[i-2]:1;
}
}
}
f[i] %= 1e9+7;
}
return (int)f[n-1];
}
}
09-28 lc437. 路径总和
- 求的是二叉树上,路径总和等于k的路径的数量
- 此处路径的约束是,只能向下,其他随便
- 所以这里是一个多路的连续序列求和问题,可以用累加和来进行解决
- 我这里写的方法是On2的,因为整体的数据量并不大,所以时间其实也还好
- 其实,这里不关注前面的序列,直接用一个hashmap来存储不不同累加和对应的结尾节点个数,加上递归的回溯,这样可以o1就算出当前节点结尾的满足题意的路径个数。
- 下面是On2的代码,懒得改了,也打败了60%多的。。
class Solution {
//求的是所有的路径数
//不对好像,有点难啊,在一条路径上,就是一个序列,那么就是求当前路径上的个数
//首先,给你一个数组,让你求连续和为k的序列个数
//然后,除此之外,每次的路径还不一样,这个倒好办,用个栈来存储当前的情况
//用个数组来存储当前的情况
//首先连续序列最大和,这个是On2的,然后其实可以整合成On的
//用两个数组,一个是序列数组,存放遍历过的值,另一个是累加和数组,存放当前累加和
private static int res;
private static int t;
private static int[] seq;
private static int[] sum;
public int pathSum(TreeNode root, int targetSum) {
res = 0;
t = targetSum;
seq = new int[1001];
sum = new int[1001];
help(root, 1);
return res;
}
//序列数组,累加和数组,当前位置数组
private static void help(TreeNode root, int index){
if(root==null) return;
seq[index] = root.val;
sum[index] = root.val + (index==0?0:sum[index-1]);
res += pathNums(index);
help(root.left,index+1);
help(root.right,index+1);
}
//计算以当前位置结尾的连续序列累加和
private static int pathNums(int index){
int cnt = 0;
for(int i=0; i<index; i++){
if(sum[index]-sum[i]==t) cnt++;
}
return cnt;
}
}
09-29 lc517. 超级洗衣机
- 这个题挺难,是个贪心
- 首先要求出平均值,对于低于平均值的,要从别处拿,对于高于的,要往别处扩散
- 遍历整个的数组,用diff来记录当前的总的差值(diff实际就代表,左边一共需要多少件,而扩散已知,是只能从一边到另一边的,所以就直接是diff次操作才行),那么当前左边和当前洗衣机要平衡,就至少需要其中的最大值
- 这里比较的时候,当前的是 m − a v g m-avg m−avg,因为如果是低于平均值的话,已经计算到左边了,必比diff绝对值小
class Solution {
//首先算累加和,然后求avg
//对于高于avg的值,它需要i-avg才能到平均值,对于低于avg的值,它需要avg-i
//所以至少他们需要这么多的次数
//然后中间如果有东西挡着,那么也无妨,可以做到传递,只要把中间的给选取,那么就可以保持中间不变,小值加1
//找到一个规律,就是小值,需要加,还挺麻烦,不好写这题
public int findMinMoves(int[] machines) {
if(machines==null||machines.length==0) return -1;
int sum = 0;
for(int m:machines) sum += m;
int avg = sum/machines.length;
if(sum%machines.length!=0) return -1;
int diff = 0, res = 0;
for(int m:machines){
diff += avg -m;
res = Math.max(res,Math.max(Math.abs(diff),m-avg));
}
return res;
}
}
09-30 lc223. 矩形面积
- 首先思路很简单,矩形的对角坐标知道,直接就可以分别求出面积
- 然后二者相加,减去二者的交集就可以得出它们覆盖的总面积,也就是并集
- 如何求交集是一个问题,首先判断是否有交集,我们以矩形1为基准,用矩形2来判断,当bx1大于等于ax2时,当bx2小于等于ax1时,二者必没有交集面积,同理y方向也是一样
- 然后再确定交集坐标,也是一样的,固定矩形1,用矩形2判断,因为此时必有交集,那么要么bx1在ax1与ax2之间,要么bx1小于等于ax1,那么此时交集矩形的x1便可以有两个情况, i n t x 1 = b x 1 > = a x 1 ? b x 1 : a x 1 ; int x1 = bx1>=ax1?bx1:ax1; intx1=bx1>=ax1?bx1:ax1;,这样就可以确定x1,同理其他的三个坐标都是这样
class Solution {
//求总面积
//直接求两个矩形分别的面经,然后再求交集的面积,然后减一下就可以了
public int computeArea(int ax1, int ay1, int ax2, int ay2, int bx1, int by1, int bx2, int by2) {
//分别求面积
int s1 = (ax2-ax1) * (ay2-ay1);
int s2 = (bx2-bx1) * (by2-by1);
//相交的面积
int s3 = computeOverlap(ax1,ay1,ax2,ay2,bx1,by1,bx2,by2);
return s1+s2-s3;
}
//计算交集面积
private int computeOverlap(int ax1, int ay1, int ax2, int ay2, int bx1, int by1, int bx2, int by2){
if(bx1>=ax2||bx2<=ax1||by2<=ay1||by1>=ay2) return 0;
//有交集
int x1 = bx1>=ax1?bx1:ax1;
int x2 = bx2<=ax2?bx2:ax2;
int y1 = by1>=ay1?by1:ay1;
int y2 = by2<=ay2?by2:ay2;
return (x2-x1) * (y2-y1);
}
//这个交集计算更清晰
private int computeOverlap(int ax1, int ay1, int ax2, int ay2, int bx1, int by1, int bx2, int by2){
if(bx1>=ax2||bx2<=ax1||by2<=ay1||by1>=ay2) return 0;
//有交集
int x1 = Math.max(ax1,bx1);
int x2 = Math.min(ax2,bx2);
int y1 = Math.max(ay1,by1);
int y2 = Math.min(ay2,by2);
return (x2-x1) * (y2-y1);
}
}