常规笔试算法 下部 应试版(笔试编程必考)

来源:互联网 发布:电脑恢复手机数据 编辑:程序博客网 时间:2024/06/05 19:48

常规笔试中必考DFS所有路径、BFS最短路径、Prim生成树、Tarjan强连通四个要点。

我发现有不少童鞋在遇到题目时根本没有成型的模板,这个简直是太蠢的做法,因为一道题20+分钟你肯定做不完了。

这里直接给出DFS、BFS(DijkstraBFS)、PrimDBFS、TarjanSCCDFS四个核心算法,笔试编程时直接套用非常有效。

特别注意本人所用的全局变量名称不允许修改,这些全都有具体的实际含义,详细注释你可以参见上一篇博客《常规图论算法  上部  练习版(练习编程必做)》,

除非你真的看过Boost Graph Library的源码(本人的写法即为其源码的C++模板改编),否则你的水平瞎几把命名那简直是垃圾。

#include <cstdio>#include <vector>#include <stack>#include <queue>#include <set>#include <algorithm>using namespace std;#define NVTX_MAX 64#define LEN_MAX 0xFFFFstruct Vtx {    int num;};struct Edg {    int vtx, adj;    int len;};int nvtx, nedg;Vtx vtxs[NVTX_MAX];vector<Edg> edgs[NVTX_MAX];int sums[NVTX_MAX];int vsts[NVTX_MAX];int dsts[NVTX_MAX];int prvs[NVTX_MAX];int npths[NVTX_MAX];void InitDsts&Prvs() {    for (int v = 0; v < nvtx; v += 1) {        vsts[v] = 0;        dsts[v] = LEN_MAX;        prvs[v] = v;    }}void BFS_VST(int v);void BFS_SCH() {    // Init()    InitVsts();    for (int v = 0; v < nvtx; v += 1) {        if (vsts[v] == 0) {            // DscCC()            BFS_VST(v);            // FinCC()        }    }}void BFS_VST(int sv) {    queue<int> vq;    vsts[sv] = 1;    // DscVtx(sv)    vq.push(sv);    while (! vq.empty()) {        int v = vq.front(); vq.pop();        for (int i = 0; i < edgs[v].size(), i += 1) {            int av = edgs[v][i].size();            if (vsts[av] == 0) {                vsts[av] = 1;                // DscVtx(av)                // TreeEdg(v,av)                vq.push(av);            } else {                // NonTreeEdg(v,av)            }        }        vsts[v] = 2;        // FinVtx(v)    }}void DFS_VST(int v);void DFS_SCH() {    // Init()    InitVsts();    for (v = 0; v < nvtx; v += 1) {        if (vsts[v] == 0) {            // DscCC()            DFS_VST(v);            // FinCC()        }    }}void DFS_VST(int v) {    vsts[v] = 1;    // DscVtx(v)    for (int i = 0; i < edgs[v].size(); i += 1) {        int av = edgs[v][i].adj;        if (vsts[av] == 0) {            // TreeEdg(v,av)            DFS_VST(av);        } else if (vsts[av] == 1) {            // BackEdg(v,av)        } else {            // FwdCrsEdg(v,av)        }    }    vsts[v] = 2;    // FinVtx(v)}struct CmpDst {    bool operator()(int v1, int v2) {        return dsts[v1] != dsts[v2] : dsts[v1] < dsts[v2] : v1 < v2;    }};// 注意Dijkstra算法基于BFS算法void DijkstraBFS(int sv) {    // Init()    InitVsts();                 // 只需初始化vsts[]即可,不必初始化dsts[],prvs[]等    set<int, CmpDst> vpq;       // 用set作优先队列,注意比较器的写法    // 起点初始化    vsts[sv] = 1;    sums[sv] = vtxs[sv].num;    dsts[sv] = 0;    prvs[sv] = sv;    npths[sv] = 1;    vpq.insert(v);    while (! vpq.empty()) {        int v = *vpq.begin(); vq.erase(v);        for (int i = 0; i < edgs[v].size(); i += 1) {            int av = edgs[v][i].adj;            int len = edgs[v][i].len;            int num = vtxs[av].num;            if (vsts[av] == 0) {                vsts[av] = 1;                // TreeEdg(v,av) {                sums[av] = sums[v] + num;                dsts[av] = dsts[v] + len;                prvs[av] = v;                npths[av] = npths[v];                // }                vpq.insert(av);            } else if (vsts[av] == 1) {                // NonTreeEdg(v,av) {                if (dsts[v] + len < dsts[av]) {                    sums[av] = sums[v] + num;                    dsts[av] = dsts[v] + len;                    prvs[av] = v;                    npths[av] = npths[v];                    vpq.erase(av);                    vpq.insert(av);                } else if (dsts[v] + len == dsts[av]) {                    if (sums[v] + num > sums[av]) {                        sums[av] = sums[v] + num;                        prvs[av] = v;                    }                    npths[av] += npths[v];      // 注意路径个数增量                }                // }            }        }        vsts[v] = 2;    }}bool Bellman(int sv) {    InitDsts(); InitPrvs();    dsts[sv] = 0;    for (int n = 0; n < nvtx; n += 1) {        bool relaxed = false;        for (int v = 0; v < nvtx; v += 1) {            for (int i = 0; i < edgs[v].size(); i += 1) {                int av = edgs[v][i].adj;                int len = edgs[v][i].len;                if (dsts[v] + len < dsts[av]) {                    dsts[av] = dsts[v] + len;                    prvs[av] = v;                    relaxed = true;                }            }        }        if (! relaxed) {            break;        }    }    // 检测负环    for (int v = 0; v < nvtx; v += 1) {        for (int i = 0; i < edgs[v].size(); i += 1) {            int av = edgs[v][i].adj;            int len = edgs[v][i].len;            if (dsts[v] + len < dsts[av]) {                return false;            }        }    }    return true;}int dsts[NVTX_MAX][NVTX_MAX];void InitDsts() {    for (int v1 = 0; v1 < nvtx; v1 += 1) {        for (int v2 = 0; v2 < nvtx; v2 += 1) {            dsts[v1][v2] = LEN_MAX;        }    }    // 点初始化    for (int v = 0; v < nvtx; v += 1) {        dsts[v][v] = 0;    }    // 边初始化    for (int v = 0; v < nvtx; v += 1) {        for (int i = 0; i < edgs[v].size(); i += 1) {            int av = edgs[v][i].adj;            int len = edgs[v][i].len;            dsts[v][av] = len;        }    }}bool Floyd() {    InitDsts();    for (int v = 0; v < nvtx; v += 1) {        for (int sv = 0; sv < nvtx; sv += 1) {            if (dsts[sv][v] != LEN_MAX) {                for (int dv = 0; dv < nvtx; dv += 1) {                    if (dsts[v][dv] != LEN_MAX) {                        dsts[sv][dv] = min(dsts[sv][dv], dsts[sv][v] + dsts[v][dv]);                    }                }            }        }    }    // 检测负环    for (int v = 0; v < nvtx; v += 1) {        if (dsts[v][v] < 0) {            return false;        }    }    return true;}// 注意Prim算法完全基于Dijkstra算法void PrimDBFS() {    InitVsts();                 // 只需初始化vsts[]即可,不必初始化dsts[],prvs[]等    set<int, CmpDst> vpq;       // 用set作优先队列,注意比较函数的写法    // 起点初始化    vsts[sv] = 1;    dsts[sv] = 0;    prvs[sv] = sv;    vpq.insert(v);    while (! vpq.empty()) {        int v = *vpq.begin(); vq.erase(v);        for (int i = 0; i < edgs[v].size(); i += 1) {            int av = edgs[v][i].adj;            int len = edgs[v][i].len;            if (vsts[av] == 0) {                vsts[av] = 1;                // TreeEdg(v,av) {                dsts[av] = len;                prvs[av] = v;                // }                vpq.insert(av);            } else if (vsts[av] == 1) {                // NonTreeEdg(v,av) {                if (len < dsts[av]) {                    dsts[av] = len;                    prvs[av] = v;                    vpq.erase(av);                    vpq.insert(av);                }                // }            }        }        vsts[v] = 2;    }}int ncc;int ccs[NVTX_MAX];int dscs[NVTX_MAX];int lows[NVTX_MAX];int nscc;int sccs[NVTX_MAX];int scc_idegs[NVTX_MAX];int scc_odegs[NVTX_MAX];void InitCCs&SCCs() {    for (int v = 0; v < nvtx; v += 1) {        ccs[v] = -1;        dscs[v] = -1;        lows[v] = -1;        sccs[v] = -1;    }    for (int i = 0; i < nscc; i += 1) {        scc_idegs[i] = 0;        scc_odegs[i] = 0;    }    ncc = 0; nscc = 0;}void CCDFSRcr(int v);void CCDFS() {    // Init() {    InitVsts(); InitCCs();    // }    for (int v = 0; v < nvtx; v += 1) {        if (vsts[v] == 0) {            // DscCC(v)            ConnCompsRcr(v);            // FinCC(v) {            ncc += 1;            // }        }    }}void CCDFSRcr(int v) {    vsts[v] = 1;    // DscVtx(v)    ccs[v] = ncc;    for (int i = 0; i < edgs[v].size(); i += 1) {        int av = edgs[v][i].adj;        if (vsts[av] == 0) {            // TreeEdg(v, av)            ConnCompsRcr(av);        }    }    vsts[v] = 2;    // FinVtx(v)}void TarjanSCCDFSRcr(int v, int &dt, stack<int> &vs);void TarjanSCCDFS() {    // Init()    InitVst(); InitSCC();    int dt = 0; stack<int> vs;    for (int v = 0; v < nvtx; v += 1) {        if (vsts[v] == 0) {            // DscCC(v)            TarjanSCCDFSRcr(v, dt, vs);            // FinCC(v)        }    }    for (int v = 0; v < nvtx; v += 1) {        for (int i = 0; i < edgs[v].size(); i += 1) {            int av = edgs[v][i].adj;            if (sccs[v] != sccs[av]) {          // 求在强连通分量图(DAG)中分量点的入度和出度                scc_odegs[sccs[v]] += 1;                scc_idegs[sccs[av]] += 1;            }        }    }}void TarjanSCCDFSRcr(int v, int &dt, stack<int> &vs) {    vsts[v] = 1;    // DscVtx(v) {    dscs[v] = lows[v] = dt; dt += 1;    // }    vs.push(v);    for (int i = 0; i < edgs[v].size(); i += 1) {        int av = edgs[v][i].adj;        if (vsts[av] == 0) {            TarjanSCCDFSRcr(av, dt, vs);            // TreeEdg(v, av) {            lows[v] = min(lows[v], lows[av]);            // }        } else if (vsts[av] == 1) {            // BackEdg(v, av) {            lows[v] = min(lows[v], dscs[av]);            // }        }    }    vsts[v] = 2;    // FinVtx(v) {    if (lows[v] == dscs[v]) {        int cv;        do {            cv = vs.top(); vs.pop();            sccs[cv] = nscc;        } while (cv != v);        nscc += 1;    }    // }}// 强连通缩点法// (1) 在DAG中从所有零入度点开始可以遍历全图,即nscc_ideg_zero,// 因为非零入度的点至少有一个其他点指向其的边可经由到达// (2) 注意题目保证强连图是只有一个连通分量的DAG,也即DAG所有点都可达、没有孤立点集,// 若有向图移除方向形成的无向图是连通图(即任意两点都有无向路径连接)则称为弱连通图// 若在弱连通图中增加max(nscc_ideg_zero, nscc_odeg_zero)条边,// 每条边从一个零出度点指向一个零入度点,则全图必定没有入度和出度为0的点,// 设...->pv->v->av->nv->...,扩展链式到所有点边,// 则存在一个相同点连接最左点和最右点,否则最左点入度和最右点出度必定为0与条件矛盾,则此图必定形成环路,// 因为只有一个连通分量,所以不存在点不在这个环路而在其他环路上,否则两个环路互不可达与条件矛盾// 结论就是弱连通图增加max(nideg_zero, nodeg_zero)则必定形成强连通图/*int nscc_ideg_zero = count(scc_idegs, scc_idegs + nscc, 0);int nscc_odeg_zero = count(scc_odegs, scc_odegs + nscc, 0);if (nscc == 1) {    printf("1\n0\n");} else {    printf("%d\n%d\n", nscc_ideg_zero, max(nscc_ideg_zero, nscc_odeg_zero));}*/

0 0
原创粉丝点击