常规笔试算法 下部 应试版(笔试编程必考)
来源:互联网 发布:电脑恢复手机数据 编辑:程序博客网 时间: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
- 常规笔试算法 下部 应试版(笔试编程必考)
- 常规笔试算法 上部 练习版(练习编程必做)
- 程序员笔试面试必考之一
- c++笔试必考内容:const使用详解
- 笔试必考-C语言之sizeof详解
- 笔试万能必考点:二叉树
- 【经典】数据库SQL笔试题目必考
- 【图说互联网面试笔试必考知识点】之排序算法复杂度/TCP/IP三次握手
- 笔试——编程&算法
- 笔试算法(2)
- 赴微软测试工程师必考一道笔试题目
- C++笔试必考内容:C/C++内存对齐
- 腾讯笔试编程题:算法基础-字符移位(C++)
- Java笔试面试题三(编程算法)
- 笔试题之算法与编程(JAVA)
- 笔试编程题(一)
- 笔试编程题(网易)
- 关于Java你应该掌握的基础知识—银行笔试必考(作者亲生经历的java试题)
- 在Android学习中有关于广播注册的问题
- Tutorial 2:你好!点 (Hello dot! )
- 关于有序广播优先级问题
- 166. Fraction to Recurring Decimal
- 【scikit-learn algorithm cheat sheet】【汉化版】scikit-learn算法选择路径图
- 常规笔试算法 下部 应试版(笔试编程必考)
- Medium 377题 Combination Sum IV
- Medium 64题 Minimum Path Sum
- 常规笔试算法 上部 练习版(练习编程必做)
- PAT考试重点真题选做(尽量参考学习)
- Master-Worker设计模式
- 【软件】system.new.dat一键解包工具,支持Android5.1
- 奶牛的锻炼
- 真有用?Snap和Flatpak 通吃所有发行版的打包方式。