5.25【A】

1871

自最后向前遍历,如果从最后往前断了,就false,不然就true

复制代码
class Solution {
public:
    bool canReach(string s, int minJump, int maxJump) {
        int n=s.size(),cur=n-1;
        if(s[cur]!='0'){return false;}
        for(int i=n-2;i>=0;i--){
            if(s[i]=='0'){
                if(cur-i<=maxJump&&cur-i>=minJump){
                    cur=i;
                }else{
                    return false;
                }
            }
        }
        return true;
    }
};

这样一次遍历不行,因为会有多条路径,这个cur只保存了一条路径,所以会漏掉一些情况从而误判

那还是bfs吧,最省事

复制代码
class Solution {
public:
    bool canReach(string s, int minJump, int maxJump) {
        int n=s.size();
        if(s[n-1]!='0'){return false;}
        vector<bool>flag(n,false);
        queue<int>q;
        q.push(0);
        flag[0]=true;
        while(!q.empty()){
            int cur=q.front();
            q.pop();
            for(int i=cur+minJump;i<=min(cur+maxJump,n-1);i++){
                if(s[i]=='0'&&!flag[i]){
                    if(i==n-1){return true;}
                    flag[i]=true;
                    q.push(i);
                }
            }
        }
        return flag[n-1];
    }
};

这代码怎么还能超时?就是全是0的情况,

但bfs每个点都只遍历一次阿,不也就On级别的复杂度?怎么对于10^5级别的数据还能超时?

如果用DP,该怎么划分子问题才能无后效性?

dpi=dpj或的和,j为i所能跳转到的那些元素;

但如果不限制上界,又何来缩减问题规模一说?

那还是从后往前遍历,还是维护一个队列,来往前寻找吗?这样才可能有多条路径返回起点

维护一个滑动窗口,就是从左侧开始,表示能到达当前i的所有元素;那他应该是i-maxJump,i-minJump;然后维护其中的0是否能被到达;当滑动窗口移动,即i向前移动时,对于右侧新增的值,如果是0,看他能否被达到,用一个flag数组来维护每个0能否被到达,如果能就把它的下标入窗口队列,不然不管;对于左侧,检测下标是否过时了,即Index<i-maxJump,如果过时了就移除;甚至应该不用窗口队列,就是维护这个窗口里所能到达的0的最大下标即可;如果这个最大下标出了窗口,即maxIndex<i-maxJump,那就直接return false;否则就一直到i为数组末尾,表示可达

我说的最大值,是说滑动窗口内部存在0且能被到达的下标的最大值index,它本身就一定是在窗口内部的,而且是不断被维护的最大值,所以当他小于i-maxJump时,就意味着后面的0一定都不可达;此外,如何完成滑动窗口的初始化,即我知道要维护[i-maxJump,

复制代码
class Solution {
public:
    bool canReach(string s, int minJump, int maxJump) {
        int n=s.size(),maxIndex=0;;
        if(s[n-1]!='0'){return false;}
        //vector<bool>flag(n,false);//[i-maxJump,i-minJump]
        for(int i=minJump;i<=maxJump;i++){
            //flag[i]=s[i]=='0';
            if(s[i]=='0'){
                //flag[i]=true;
                maxIndex=i;
            }
        }
        for(int i=maxJump+1;i<n;i++){
            cout<<"cur i:"<<i<<endl;
            if(maxIndex<i-maxJump){return false;}
            //if(maxIndex>i-minJump){return false;}
            if(s[i]=='0'){maxIndex=i;}
        }
        return true;
    }
};

这个确实就是太近所导致的

就是应该要保证这个维护的值,确实是该在窗口里

入队的位置是在i-minJump,出队的位置是在i-maxJump

我现在对于出队的处理,就是if(maxIndex<i-maxJump){return false;}阿,这个maxIndex就是指窗口当中那个最不可能被淘汰的计数,即cnt=1时的情况,如果中间的某个窗口存在cnt=0,那么后面的0不都到不了吗,而且能保证这个cnt是能被此时的i访问到的

就是说只保留最右侧满足条件的话,相当于只保留了一条路径,不过由于本身是I的滑动窗口,所以本身也没什么问题;

但对于

错的就是几种情况,就是一开始窗口初始化的时候,是否应该给maxIndex赋值,如果赋值的话,那肯定是赋值成maxJump,这个值,对应的是哪个下标的?说不清楚

如果不赋值,那么遍历的时候,从哪里开始遍历?之前是maxIndex+1,如果从MaxIndex,那这个下标的窗口就是0,maxIndex-minIndex

即,如果还是从MaxIndex-minIndex来移动右端点赋值的话,一开始的maxIndex是0,就会忽略掉中间的可能最大值

所以对于maxJump的起点的情况,对于这个下标的滑动窗口,要完整判断其窗口内的所有元素,因为除了其最有端点maxJump-minJump,其前面的所有点,都是没有元素来检验入队过的

这个样例没过是因为,当前的0确实是不可达的,是不能被其窗口里的值所能到达的,但当前的0不能被到达,不意味着到达不了终点

就是前面,比如前一个0可达,那就可以到达比当前这个0更远的地方,所以这个提前终止的判断就是不对的

复制代码
class Solution {
public:
    bool canReach(string s, int minJump, int maxJump) {
        int n=s.size(),maxIndex=0;;
        if(s[n-1]!='0'){return false;}
        vector<bool>flag(n,false);//[i-maxJump,i-minJump]
        for(int i=minJump;i<=maxJump;i++){
            //flag[i]=s[i]=='0';
            if(s[i]=='0'){
                flag[i]=true;
                //maxIndex=i;
            }
        }
        for(int i=0;i<=maxJump-minJump;i++){
            if(flag[i]){maxIndex=i;}
        }
        for(int i=maxJump;i<n;i++){

            cout<<"cur i:"<<i<<"当前元素为"<<s[i]<<"cur maxIndex"<<maxIndex<<endl;
            if(s[i-minJump]=='0'&&flag[i-minJump]){maxIndex=i-minJump;}
            if(s[i]!='0')continue;
            if(maxIndex<i-maxJump){return false;}
            //if(maxIndex>i-minJump){return false;}
            //if(s[i]=='0'){flag[i]=true;}
            flag[i]=true;
        }
        return true;
    }
};

class Solution {
public:
    bool canReach(string s, int minJump, int maxJump) {
        int n=s.size(),cnt=0;;
        if(s[n-1]!='0'){return false;}
        vector<bool>flag(n,false);//[i-maxJump,i-minJump]
        flag[0]=true;
        for(int i=minJump;i<=maxJump;i++){
            if(s[i]=='0'){
                flag[i]=true;
            }
        }
        for(int i=0;i<=maxJump-minJump;i++){
            if(flag[i]){cnt++;}
        }
        for(int i=maxJump+1;i<n;i++){
            //cout<<"cur i:"<<i<<"当前元素为"<<s[i]<<"cur maxIndex"<<maxIndex<<endl;
            if(flag[i-maxJump-1]){cnt--;}
            if(flag[i-minJump]){cnt++;}
            flag[i]=(cnt>0)&&s[i]=='0';
        }
        // for(int i=0;i<n;i++){
        //     cout<<flag[i]<<" ";
        // }
        return flag[n-1];
    }
};

319

就是统计每个数的因数数量

如果是奇数,最后就是开,否则就是偶数

如何快速求解因数的数量?如何证明完全平方数的因数数量是奇数?

树上背包

那如何知道处理顺序?就是能够在处理完3和4后,找到1,处理完1后找到2

如果DP,那dpij应该表示?表示在能选j个课的情况下,由i为根节点所展开的子树,所能达到的最大值

如果他有两个子节点l和r

如果由i展开,那i根节点必然被选择,

所以dpij=max(dplj-1,dprj-1)+numi

然后对于定值,就是

先构建树,如何构建树?

然后对这个树进行后序遍历,在叶子节点,那么就是dpij=numi

最后返回根节点的dp值

那就是说再加一重循环,从0到j-1,表示分给左子树的任务数量

即dpij=max(for 0->j-1)(dplk+dprj-1-k))+numi

不应该就一个循环码?对于每个根节点,这个v是什么?什么叫分配给前面子树的容量?

就是说,这个数不是二叉树,而是可能有多个孩子的,有多个子树的

那确实是好几重循环

先是遍历每个孩子节点,然后确定这个孩子节点要花几个容量

怎么列式?好复杂,

假设根节点有ABCD等子节点,根节点有J个容量,合并是什么意思?看起来,有多少个孩子,就有几个循环,循环的内容就是确定分给每个孩子的容量数量,其加和为j-1,即对于A,有a为分给它的数量,从0循环到j-1,然后有b为分给B的数量,从0循环到j-1-a,直到最后一个孩子节点

然后DP就是

dpij=max(dpAa+dpBb+dpCc+dpDd+numi,dpij)

这个循环,就是说是除了自己以外的其它孩子节点所分到的容量数量

但是这样,如何体现出各种孩子之间的组合?

就是说当在遍历孩子A的时候,给自己分的是k,那么分给其它孩子的容量就是v-k,那就必须得知道dpiv-k

目前每个孩子的dp\[\]\*都是知道的,因为是后序处理,所以当处理到根节点时,子节点的DP都已经确定了;

每个孩子下的容量v是什么物理含义?不应该就是分给某个孩子的容量吗?取值不就是0到j-1吗

即给自己k个,与给其他人j-1-k个的组合

知道了给其他人j-1-k个时的最佳组合,那最优就是max(dpAk+其它孩子在j-1-k时的最优组合+numi,dpij)

感觉说白了还是当成了一个二叉树处理了,即看成了自己和别人,即使这个别人可能包含多个元素

但不对吧

这样看不就一个循环,即分给这个孩子的容量v的循环吗?为什么内部还有k的循环,k是什么意思?

我现在想的DP过程就是,

遍历所有孩子

然后DPij=max(dpij-1-v,dp这个孩子v+dpiv+numi)

不过这样的话,dpiv就没被赋值,????

但是这个应该是更顶层要做的事吧?不对,好像就是本层要确定的?好乱

那如果给定dpij后,dpi的0到j-1都要自己确定,那整个树上DP的进行过程是怎样的?

就是先构建了一颗依赖树,然后说只能上K门课

对于根节点,要确定根节点的DP值,就要知道所有孩子节点的DP值,那在确定孩子节点DP值的时候,对于容量,应该是怎样的范围?

就是DP赋值的顺序和上限是怎么确定?

这是对于树形的情况,如果是图上的情况,怎么搞?就是每个节点的入度和出度最大不再是1,而可能是更大?

那对于森林的情况,如果加上了学分为0的0号根节点,那意味着在原来的M门课基础上,要再多修一门0号课程呗,这也是为什么要用m+2而不是m+1的原因吗?;但是之前的合并写法,将对根节点访问也包含了,这是否意味着对根节点的访问方案是可以被换掉的?就是相较于访问根节点,将这个容量全部给到子节点上会取得更高的收益情况下,会更新DP吗?这样的话不就违背了,就是说必须强制访问根节点的

线段树

现在是直接给定的一个数列;对于长度为n为的数列,就是以N/2,即中点来划分左右子树,前一半是左子树,后一半是右子树;

对于x,y区间上加上k的操作,判断y与中点mid的关系,如果小于,就说明全部都在左区间上

否则有在有区间的部分

再判断x与中点mid的关系,如果大于,就说明全在右区间上

两个判断都不满足,那就说明左右都有,对于左区间,应该是x,mid;右区间应该是mid+1,y

那对于修改,好像还是一个一个地去修改树上的节点,还是On

对于求和,把递归的部分加起来

不过加速的部分在哪?如果是y刚好等于mid的情况,应该可以不再递归,x等于mid+1的情况也是

即取等时的情况

就是说N是树的底层叶子节点,先是要保证把它扩充成2的整数次幂,最差情况是刚好过了1

为2N即可,

现在知道了N个叶子节点,怎么由这N个节点构筑出来线段树?

相关推荐
JieE21210 小时前
LeetCode 104. 二叉树的最大深度|递归思路超详细拆解
javascript·算法
用户35218024547515 小时前
当 Prompt 学会"热更新":Spring Boot × Nacos3 AI 实战
java·spring boot·ai编程
vivo互联网技术15 小时前
CVPR 2026 | 全新强化学习框架 BeautyGRPO:重塑真实人像
算法·大模型·cvpr·影像
Darling噜啦啦16 小时前
列表转树算法深度解析:从 Map 到 Reduce 的两种实现,面试高频考点
数据结构·算法·面试
东坡白菜18 小时前
破局全栈:一个前端开发的Java入门实战记录(1)
java·全栈
唐青枫18 小时前
Java Tomcat 实战指南:从 Servlet 容器到 Spring Boot 部署
java
wsaaaqqq19 小时前
roudan:自由选择实体、灵活操作数据、快速写入数据库的 Java 框架
java
用户4978630507319 小时前
(一)小红的数组操作
算法·编程语言
怕浪猫1 天前
Electron 系列文章封面图
算法·架构·前端框架