Dreyfus模型

Dreyfus模型将学习的过程分为五个不同的阶段或水平:

1.新手(Novice)需要详细的指导——要手把手地教。新手不知道这些指导是否有效,或者哪些指导更加重要;因为没有上下文知识可供他们使用进行评估。因此,新手需要频繁迅速的成就感和有规律的反馈。一本好的入门指导书籍要提供有足够多的图画和充足的可靠信息。

2.高级初学者(Advanced Beginner)对基本步骤——单独的任务——已经熟悉了,而且可以把它们进行有机的组合。高级初学者仍然在很大程度上是面向任务而不是面向目标的,不过他们已经开始有些概念了。这也是一个学习者最危险的阶段——他们知道自己学到的已经不少了,但是这还不足以让他们远离麻烦!刚学走路的孩子在很多方面都是高级初学者。有了足够的经验,高级初学者就能拥有足够的能力以胜任某些工作。

3.在胜任(competent)水平上,他们就走到面向目标阶段了。他们可以组合一系列任务以达成某个目标。也许任务的组合顺序不是最佳的,但是通常都可以发挥作用。有能力的人希望给定一个目标,然后能够得到别人的信任来达成这个目标。相反,如果要是试图详细告诉他们应该怎么做,这些有能力的人就会觉得很烦躁,就像是汽车里被坐在后面座位的乘客指手画脚的司机一样。大部分人在大部分技能上很难超越“胜任(competent)”水平,即使他们在每天的日常工作中使用这些技能。这是人类的基本特性——一旦有所收获,我们就不想再投入精力了,而且对于大部分活动来说,所谓的收获只不过是把工作做完而已。

注:已经能够分解目标和组合一系列任务来完成目标,这是胜任的关键。

4.在精通(Proficient)水平上,解决方案开始在人的心目中“慢慢浮现”——而且通常已经完全成型。他们已经具备了在直觉中形成解决方案主要部分细节的能力,之后就可以根据自己先前的经验积累来对解决方案进行映射。一个精通的人需要对其行动的上下文有更广阔的了解,并且开始享受隐喻和格言(以及相反的类似内容)带来的乐趣。他们仍然会回头根据接受的基于规则的训练,来验证自己行为的正确性;但在这个阶段他们已经学着相信自己的判断了。从“新手”发展到“胜任”阶段基本上是线性的过程,而到“精通”阶段代表了一个台阶的提升。一个人必须积极选择才能促成这个转变的开始。通过对某件事情重复足够的次数是可以达到“胜任”的,但要变想得“精通”,必须要有明确的心理诉求才行。

注:在精通阶段,从规则(Rule)到直觉(Intuition)的质的变化,已经能够将显性的知识转化为自己的宝贵经验和方法论。在这里是需要明确的心理述求和自我领悟。

5.专家(Expert)水平:正如从“胜任”到“精通”的转变一样,转变为“专家”也是非线性的过程。要想成为某个领域的专家,可能要花费数年的努力才能达成。这些人工作时几乎完全是从直觉自发的状态,而且很少犯错误。专家生活在模糊的世界之中。她以自己的能力为傲,而且喜欢通过与其他专家交流来校正和提高自己的技能。有趣的是,处于初级阶段的人们倾向于过高估计自己的能力,而在较高阶段的人则更加谦逊。

注:完全的融会贯通,自觉自发,形成了自我的解决问题的方法论和模式,往往已经是无招胜有招。

以上内容转自:新浪博客

翻了翻dd学长的twitter看到这个东西,很有感触。

实际上很多时候我们解决问题的方法都是将一些已有知识进行组合。而这个组合的能力,则决定了自己到底处于哪个层次。

在以前,我是很享受那种想出新颖的方法解决某个问题的感觉的。随着后来的成长,我渐渐发现我所谓的新颖方法肯定是借鉴了某些类似的经验或者思想的。有时候所借鉴的思想甚至可以和这个领域看似毫无关系。

这也就是所谓的知识结构。完全逃离自身知识结构的创新我觉得是不太现实的。我感觉即使是很牛的人,能做的往往也是从自己的知识结构体系中取出内容进行组合。

所以结论是我所该做的内容有两个:一是提升知识量的大小;二是提高对知识组合的能力。

个人看法,欢迎讨论。

注重积累

每次回家和父亲聊天都感觉自己实在太弱了。
探寻原因,终究是积累上的差距实在太大。
即使我能很快明白原理,也没办法做和他一样的事情。
概括而言,自己需要注重的积累包括这几个方面:

  1. 专业知识的积累。在实践中注意观察底层的运作方式,知道其中利害。思考的过程中,注意了解相关法律。知悉什么能做,什么不能做,还有就是:什么说不清楚能不能做,又没什么人敢做。
  2. 知识面上的积累。通识上的差距。与各行各业的人均应有交集,遇人需要有共同的话题以及比较独到的见解。
  3. 交际技巧上的积累。首先是礼仪:端茶递水、敬酒、倾听、谈话、问候、祝福、送礼等,其实自己总结来看,就是七个字:不以自我为中心。再者是比较适合交际的活动:高尔夫、桌球、羽毛球、乒乓球、斗地主等。
  4. 经济实力上的积累。无话可说……
  5. 人脉和社会认同程度上的积累。亦即有头有面。在这基础上可以做的事情就太多了。但是这一点是以前面的内容为基础的。没有前面的支撑,一味地追寻人脉,得到的很可能也只是镜花水月吧。

真希望10年后我不再是今日捉襟见肘、纸上谈兵的赵括。
也希望以后我的小孩能接受到至少和我相当的指引和教育。
:Happy:

CERC2013 Problem E 勇者斗恶龙

地址:http://codeforces.com/gym/100299(要登录了点进gym才能成功传送)
很有意思的一个Problem。

一棵树上,勇者在1号点,初始血量是0。每个点有一个权值,踩上去血量会加上$$delta_i$$,这个值可正可负。勇者任何时候血量不能低于0。问勇者能否到达某个目标点T。
国际惯例$$1 \leq n \leq 10^5, -10^6 \leq delta_i \leq 10^6$$

看了solution以后才写出来的。
其实看之前已经有一些零零碎碎的idea。就是组织不起来。

  1. 按道理可以把结果组织成带拓扑关系的pair的形式。pair的第一个元素表示至少要多少血量才能进去,第二个元素表示进去以后能增加多少血量。通俗说就是每个血泉的门槛。
  2. 进去以后血量会更亏的地方没有必要走进去
  3. 应该转化成最多能获得多少血量的问题。

先看最后一点,转化的方法是从需要到达的点连一条边到一个新的点,这个新的点值是无穷。最后只要看最多能获得的血量是不是达到无穷就行了。

然后剩下的就是solution最神棍的地方。在求解“最多能获得多少血量”的前提下,因为最优性的原则可以把一棵树化归为一种理想结构:拓扑关系为一条链的pair。并且满足上面提到的第2条(即每对pair里加血总比扣血多),以及依赖越浅的pair,门槛越低。也就是说,把他看成数组的话,门槛递增。

证明分两步。

  1. 证明:假设是一条链的依赖关系,那么可以视作以上提到理想结构。

    1. 显然一条链上相邻的正值和相邻的负值可以合并。于是必然正负交错,可以构成pair。
    2. 依赖关系最深的pair(就是最后一个),如果first >= second,那么可以直接扔掉。因为根据最优性,永远不会选它。
    3. 对于相邻的pair,如果previous.first >= previous.second(扣血比补血多),那么这个pair必然可以和后面一个合并。原因是根据最优化目标的原则,不可能取了previous就不取next。所以previous和next要么同取要么同不取,所以理应合并。
    4. 对于相邻的pair,如果previous.first >= next.first(伤血门槛不递增),那么previous和next必然可以合并。原因是:根据前面两条,所有的pair都满足first < second,所以根据最优性能取都取掉最好。所以都是贪心去取就好了。如果next门槛比previous还低,那么做完previous肯定顺便做掉next。其实这里条件可以更紧一点,取previous.first >= preivous.first – previous.second + next.first作为合并条件也可以AC的,但是前面那个已经足够了。
  2. 证明:假如每个分支下的子树都是链,那么该子树也可以看成链。
    其实就是数学归纳法的意思。然后这个只要把每个分支下的pair按first顺序列成一行就可以了。

综上,用multiset<pair <long long, long long>>维护链就可以了。

#include <cstdio>
#include <vector>
#include <set>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef pair <ll, ll> PLL;
typedef multiset <PLL> MS;
#define rep(i,n) for (int i = 0; i < (int)(n); i++)
#define foreach(it,v) for (__typeof((v).end()) it = (v).begin(); it != (v).end(); it++)
const int N = 200005;
const ll INF = 1e15;
int Tc, n, m;
vector <int> E[N];
MS pool[N];
ll a[N];

MS* merge(MS *a, MS *b) {
    if (a->size() < b->size()) swap(a, b);
    foreach (it, *b) a->insert(*it);
    b->clear();
    return a;
}

MS* dfs(int u, int fa) {
    MS *res = &pool[u];
    rep (i, E[u].size())
        if (E[u][i] != fa)
            res = merge(dfs(E[u][i], u), res);
    PLL cur = a[u] > 0 ? PLL(0, a[u]) : PLL(-a[u], 0);
    while (!res->empty() && (cur.second < cur.first || cur.second >= res->begin()->first)) {
        PLL tmp = *res->begin();
        res->erase(res->begin());
        cur = tmp.first > cur.second ? PLL(cur.first - cur.second + tmp.first, tmp.second) : PLL(cur.first, cur.second - tmp.first + tmp.second);
    }
    if (cur.second > cur.first) res->insert(cur);
    return res;
}

int main() {
    scanf("%d", &Tc);
    while (Tc--) {
        scanf("%d%d", &n, &m);
        rep (i, n + 1) E[i].clear();
        rep (i, n) {
            int x;
            scanf("%d", &x);
            a[i] = x;
        }
        rep (i, n - 1) {
            int u, v;
            scanf("%d%d", &u, &v);
            u--; v--;
            E[u].push_back(v);
            E[v].push_back(u);
        }
        --m;
        E[m].push_back(n);
        E[n].push_back(m);
        a[n++] = INF;
        MS *res = dfs(0, -1);
        ll hp = 0;
        bool flag = 0;
        foreach (it, *res) if (hp >= it->first) {
            hp += it->second - it->first;
            if (hp >= INF / 2) flag = 1;
        }
        res->clear();
        puts(flag ? "escaped" : "trapped");
    }
    return 0;
}