最近开始日常住力扣刷题了,采用的模式是在自己零碎的时间看看题和对应的题解,然后在自己学习的时间块里抽时间做两道题目。近一个月做的题慢慢变多,现在关于做题也有了一些新的想法。
现在做的题基本都是简单或者详细看过答案的,所以做题时需要做的不是从零开始想,而是把一个模糊的正确思路给理清,然后编成代码。之所以采用这种模式还是为了提高学习效率,如果把学习和练习的过程分开,那么需要先学习,即看这个知识点的理论、详细的原理,然后系统学会后再去做题。这样分开后,两块都会占用很大的时间,而且在学习过程中缺少练习容易造成理解不够透彻、学习效率低,练习时因为缺少训练和编程技巧,解题也会变得异常慢。
虽然是先看的题解,但是看题解也是讲究一些方法的。首先当天写的题一般都是两三天前看的答案,并不是刚刚看过答案,防止短时记忆干扰对解题思路的思考;其次,看题解主要是看其思路的关键点。经常做算法题的应该都会发现,只有理清问题的几个解题关键点,题目才很容易写出来。这样的关键点对于一个题通常有1个或者4、5个这么多,不会特别多,缺少对其中任何一个关键点的理解都会对解题造成很大影响,相反,这些关键点都已经串通了的话,整理思路和编代码就变的容易多了。
即便是一周前看的题解,现在再去写这道题,这和从零开始写一道题本质还是不一样的。因为从零开始写题,就发现各种问题都暴露出来了,最快遇到的问题就是题目理解不透,如果先看题解的话,题解本身就能辅助理解题目,脱离了题解,光是把题目的意思理清、各种限定界限认识到就需要费一番功夫了;其次,这种从零开始找方向思考,和复现一个模糊的可行解区别还是很大的,从零开始思考,就是完全调用自己的知识库,去分析可能的可行解了,走的每一个方向都不太能保证是能解出来问题的。而复现模糊可行解,至少你明白走的方向没错,即便没写出来也不会认为方向走错了。
我觉得具体采用什么方法还得基于客观、合理的评估自己的能力。近一个月来练习了小200到题,不说没见过的题,如果碰到和我做的这些题中解法比较类似的题,我大概率是能写出来的(比如股票投资1,2,3都做了,今天偶然发现还有4,虽然是困难题,但是很快就写出来了),再去重复写原题,我认为能限时写出来的概率高于95%(就是竞赛or考试中出原题能写出来)这个概率应该会更高。对于一些想法比较新颖但是思路有点绕的题也做了收藏,日后等自己非内化的部分忘掉之后再去看这些题,以做到思维的内化而不是简单记忆。同时,还要注意总结学到的知识和方法,恰好我之前冲过mindmaster的会员,所以现在每做一道题,如果用到了我之前从未见到过的数据结构或者算法,我就会记在思维导图上。思维导图的内容不详细但很密集,基本是全覆盖我遇到过的巧妙数据结构和算法。比如记一个单调栈,只需要记下英文名mono_stack和其一个功能是O(n)时间内找出一个序列所有元素的双边第一个最大/最小元素。或者dummyhead,功能是不用反复判断确定不稳定的头结点。只要依稀记得有这个功能,遇到具体问题再去复现这个模糊的功能就可以了,而不用把什么都详细的记下来。基于这些,我认为我的这种模式是很高效的,短时间的真正的学会了大量东西,同时也对学到的东西有一定认识。虽然缺少实战能力,但是如果不采用这种模式,我想结果是也没真正的把实战能力练强,同时也没学到太多东西,一直都是低效的思考。
这种模式我认为适合非大佬、时间不是特别充裕的人(没法每天都整天的时间研究算法)。对于大佬,我也不太了解,只是本科时候参加acm时,我简单题还在努力的码代码,个别佬中等题都已经ac了。我觉得对于他们当然不适用这种方法,因为他们学的已经够多了,相比之下更缺实战能力(相比他们自身哈),怎么在限时环境中克服各种可能的内外在的意外和障碍,快速的解出来题,应该是他们最关心的。就像我高中写数学或者物理一样,可以说基本没有什么题不是三四个小时之内写不出来的,但是有很多题是短时间内写不出来的,这就导致考试的时候还是写不出来。
我觉得解算法题,通常不是想当然的漫无边际的思维发散的去想可能的解法,也不是凭直觉死磕一条感觉有希望的路,而是去寻找解题的大方向,然后利用一个个小块的数据结构与算法组件,拼成一个适合解这道题的完整的算法。就好像搭积木一样,有时候搭不成是缺少积木(比如完全不知道有数字电路这种东西)或者小组件(不知道矩阵元素一位相对位置和二维坐标之间怎么转换),有时候是有积木不会搭(dp不知道怎么初始化边界条件)。我认为积木和小组件这种东西完全不要自己想,要去学。搭积木的方法可以独立思考,想不出来就多看答案,多练。如果不采用这种模式,而是想当然的凭感觉或者思维极其发散,通常是意义不大的,偶尔练习一下保证思维的创新能力还行,但要是日常研究算法、刷题采用这种方式,我感觉完全是得不偿失。
233. 数字 1 的个数初看这道题感觉有思路,就去写了。但是逻辑越想越碎,代码越写越臃肿,各种打补丁,逢山开路、遇水架桥,经过一个多小时的努力,算是ac了,但是我感觉纯浪费时间。官解的方法只有短短几行,而且下回再遇到这个题,我估计我大概率还得花至少半个小时才能写出来,因为解这道题瞬时产生的那些“逢山开路、遇水架桥”的方法我都不记得了,整个题写的都比较混乱。
代码
class Solution {
private:
typedef pair<int,int> pii;
vector<pii>quality;//k位数1的总个数 含1的数字的数量
void calculate(int k)
{
int res=0;
int coefficient=1;
for(int i=1;i<=k;++i)
{
coefficient*=(k-i+1);
coefficient/=i;
res+=coefficient*i*pow(9,k-i);
}
quality[k].first=res;
quality[k].second=pow(10,k)-pow(9,k);
}
pii count(int n,int k)
{
if(k<=1)return {k,k};
pii ans={0,0};
int high=n/pow(10,k-1);
for(int i=0;i<high;++i)
{
ans.first+=quality[k-1].first;
if(i==1)
{
ans.first+=pow(10,k-1);
ans.second+=pow(10,k-1);
}
else
ans.second+=quality[k-1].second;
}
int kk=0;
int num=n-high*pow(10,k-1);
while (num)
{
num/=10;
++kk;
}
num=n-high*pow(10,k-1);
pii tmp= count(num,kk);
ans.first+=tmp.first;
if(high==1)
{
ans.first+=num+1;
ans.second+=num+1;
}
else
ans.second+=tmp.second;
//cout<<n<<'\t'<<k<<'\t'<<ans.first<<'\t'<<ans.second<<'\n';
return ans;
}
public:
int countDigitOne(int n) {
int num=n,k=1;
while (num)
{
++k;
num/=10;
}
quality.assign(k,{0,0});
for(int i=1;i<k-1;++i)calculate(i);
num=n;
return count(num,k-1).first;
}
};因为现在非实战能力也没有,所以要多学习,少漫无目的的思考。先不必担心实战能力,关于实战能力,需要把各种数据结构、算法的都学会,各种技巧都掌握的差不多了(这个时候就是非实战能力强,实战能力弱),再去有意的锻炼实战能力,否则是本末倒置、得不偿失、赔了夫人(时间花了)又折兵(基本是啥提高没有)。