王道考研机试指南重写

  • 前言
  • 数据结构一章
    • 栈的应用
      • 括号匹配问题
      • 简单计算器
    • 哈夫曼树
      • 哈夫曼树
    • 二叉树
      • 二叉树遍历
    • 二叉排序树
      • 二叉排序树
      • 二叉搜索树
  • 图论一章
    • 并查集
      • 畅通工程
    • 最小生成树
      • 还是畅通工程
      • Freckles
    • 最短路径
      • 最短路
      • 最短路径问题
    • 拓扑排序
      • Leagal or Not
  • 动态规划一章
    • 递推求解
      • N 阶楼梯上楼问题
    • 背包
      • 采药
      • Piggy-Bank
      • 珍惜现在,感恩生活

前言

几年前为了应付有些学校的机试,大概刷了一遍《王道考研机试指南》这本书里的代码王道考研机试指南代码合集。几年过去了想重新刷一遍,加强一下算法能力,同时也熟悉c++的新特性。以下代码不注重算法性能,而是尽可能使用一些新特性并保持代码规范(尝试规范)Google 开源项目风格指南。

代码随缘写,章节也不固定
代码环境:linux vscode

Makefile文件(改别人博客上的)

.PHONY: all clean     # .PHONY后面的target表示的也是一个伪造的target, 而不是真实存在的文件target
SRC=$(wildcard *.cpp)      # 获取工作目录下的所有的.cpp文件列表
BIN=$(SRC:%.cpp=%)          # 替换扩展名
CC=g++
# -Wall   生成常见的所有告警信息
# -g  用于在生成的目标可执行文件中,添加调试信息,可以使用GDB进行调试
# -o 用于连接生成可执行文件,在其后可以指定输出文件的名称
# -c 用于把源码文件编译成 .o 对象文件,不进行链接过程(之前误用了这个选项,编译完后运行显示没有权限)
CXXFLAGS=-Wall -g  -std=c++2a -oall:$(BIN)# $^ 指代所有前置条件,之间以空格分隔
# $@ 指代当前目标,就是Make命令当前构建的那个目标
$(BIN):%:%.cpp$(CC) $^  $(CXXFLAGS)  $@clean:rm  $(BIN)

数据结构一章

栈的应用

括号匹配问题

题目链接

#include <iostream>
#include <stack>
#include <string>class Solution {public:bool IsMatch(const std::string &s) {std::stack<char> symbol_stack;std::string left_bracket = "{[(";std::string right_bracket = "}])";char top_char;for (auto c : s) {if (left_bracket.find(c) != left_bracket.npos) { // 若为左括号symbol_stack.emplace(c);} else {auto pos = right_bracket.find(c);if (pos != right_bracket.npos) { // 若为右括号if (symbol_stack.empty()) {return false;}top_char = symbol_stack.top();if (top_char != left_bracket[pos]) { // 与栈顶的符号不匹配return false;}symbol_stack.pop();}}}return true;}
};int main() {Solution solution;std::string input;std::cin >> input;bool ans = solution.IsMatch(input);if (ans) {std::cout << "true" << std::endl;} else {std::cout << "false" << std::endl;}
}

简单计算器

题目链接

// simple_calculator.h
#include <iostream>
#include <stack>
#include <string>
#include <tuple>class Solution {public:void SetExpr(const std::string &expr);double GetExprValue();private:auto GetNumber(std::size_t pos) const;int GetOpPriority(char op) const;void ProcessSymbol(char symbol);std::string expr_;std::stack<char> symbol_stack_;std::stack<double> number_stack_;std::string operators_;char markup_char_{'m'};  // 特殊运算符
};
// simple_calculator.cpp
#include "simple_calculator.h"// 从字符串pos位置开始读取数字,直到读到空格
// 返回转换的数字与空格下标
auto Solution::GetNumber(std::size_t pos) const {double res = 0;while (expr_[pos] != ' ') {res = res * 10 + expr_[pos] - '0';pos++;}return std::make_tuple(res, pos);
}
// 返回运算符的优先级
int Solution::GetOpPriority(char op) const {if (op == '*' || op == '/') {return 3;}if (op == '+' || op == '-') {return 2;}if (op == markup_char_) {  // 特殊运算符返回最低优先级return 1;}return 0;
}
// 对运算符进行处理
void Solution::ProcessSymbol(char symbol) {int symbol_priority = GetOpPriority(symbol);char top_symbol = symbol_stack_.top();int top_priority = GetOpPriority(top_symbol);if (symbol_priority > top_priority) {  // 大于栈顶运算符优先级,入栈symbol_stack_.emplace(symbol);} else {  // 将栈顶运算符出栈,进行相应的运算if (top_symbol == markup_char_) {  // 代表表达式字符串遍历到了末尾return;}double lhs, rhs, res;rhs = number_stack_.top();number_stack_.pop();lhs = number_stack_.top();number_stack_.pop();switch (top_symbol) {case '+': {res = lhs + rhs;break;}case '-': {res = lhs - rhs;break;}case '*': {res = lhs * rhs;break;}case '/': {res = lhs / rhs;break;}}number_stack_.emplace(res);symbol_stack_.pop();ProcessSymbol(symbol);  // 循环处理}
}
void Solution::SetExpr(const std::string &expr) {expr_ = expr + " " + markup_char_;  // 在表达式末尾加上" m"operators_ = "+-*/";operators_ += markup_char_;while (!symbol_stack_.empty()) {symbol_stack_.pop();}symbol_stack_.emplace(markup_char_);  // 将特殊运算符压入栈中while (!number_stack_.empty()) {number_stack_.pop();}
}
double Solution::GetExprValue() {std::size_t expr_len = expr_.length();for (std::size_t i = 0; i < expr_len; i++) {if (operators_.find(expr_[i]) != operators_.npos) {ProcessSymbol(expr_[i]);} else if (expr_[i] != ' ') {  // 数字部分auto [res, new_pos] = GetNumber(i);i = new_pos;number_stack_.emplace(res);}}return number_stack_.top();
}int main() {Solution solution;std::string expr;double ans;while (getline(std::cin, expr)) {if (expr.length() == 1 && expr[0] == '0') {break;}solution.SetExpr(expr);ans = solution.GetExprValue();printf("%.2f\n", ans);}return 0;
}

在这里我将代码写成两个文件一是因为函数比较多,全部写在类里有点难看。二是因为GetNumber函数的返回类型使用的是自动类型推导,但当GetExprValue调用它时,auto还没有进行推导过程,会报错

use of 'auto GetNumber(std::size_t pos) const;' before deduction of 'auto'

哈夫曼树

哈夫曼树

题目链接

#include <array>
#include <iostream>
#include <queue>
#include <vector>
class Solution {public:int GetWeight(const std::vector<int>& arr) {// 小顶堆std::priority_queue<int, std::vector<int>, std::greater<int>> queue;for (auto item : arr) {queue.emplace(item);}int weight = 0;int m, n;while (queue.size() > 1) {  // 取出两个最小的数字,将其和放入堆中m = queue.top();queue.pop();n = queue.top();queue.pop();queue.emplace(m + n);weight += m + n;  // 所有非叶节点权值之和即为哈夫曼树权值}return weight;}
};int main() {Solution solution;int n;while (std::cin >> n) {std::vector<int> arr;arr.resize(n);for (int i = 0; i < n; i++) {std::cin >> arr[i];}int ans = solution.GetWeight(arr);std::cout << ans << std::endl;}return 0;
}

可以声明数组时直接指定大小std::vector arr(n);,但我觉得resize更好看一些。

二叉树

二叉树遍历

题目链接

#include <iostream>
#include <memory>
#include <string>class BinaryTree {struct Node {  // 二叉树节点Node(){};Node(std::shared_ptr<Node> lchild, std::shared_ptr<Node> rchild,char content): lchild_(lchild), rchild_(rchild), content_(content){};std::shared_ptr<Node> lchild_;std::shared_ptr<Node> rchild_;char content_;};using LinkNode = std::shared_ptr<Node>;public:void BuildTree(const std::string& preorder, const std::string& inorder) {root_ = BuildTreeImpl(preorder, inorder);postorder_string_ = "";}std::string GetPostorderString() {PostorderVisit(root_);  // 进行后序遍历,生成相应字符串return postorder_string_;}private:LinkNode BuildTreeImpl(const std::string& preorder,const std::string& inorder);void PostorderVisit(LinkNode node);LinkNode root_;                 // 二叉树根节点std::string postorder_string_;  // 后序遍历字符串
};BinaryTree::LinkNode BinaryTree::BuildTreeImpl(const std::string& preorder,const std::string& inorder) {if (preorder.empty()) {return {};}auto len = preorder.length();char root_char = preorder[0];// 将前序字符串切分成根 左子树 右子树,将中序字符串切分成左子树 根 右子树auto pos = inorder.find(root_char);auto pre_left = preorder.substr(1, pos);auto pre_right = preorder.substr(pos + 1, len - pos - 1);auto in_left = inorder.substr(0, pos);auto in_right = inorder.substr(pos + 1, len - pos - 1);auto lchild = BuildTreeImpl(pre_left, in_left);    // 构建左子树auto rchild = BuildTreeImpl(pre_right, in_right);  // 构建右子树auto node = std::make_shared<BinaryTree::Node>(lchild, rchild, root_char);return node;
}
// 进行后序遍历,并将结果保存至字符串中
void BinaryTree::PostorderVisit(LinkNode node) {if (node == nullptr) {return;}PostorderVisit(node->lchild_);PostorderVisit(node->rchild_);postorder_string_.push_back(node->content_);
}
int main() {BinaryTree tree;std::string preorder;std::string inorder;while (std::cin >> preorder >> inorder) {tree.BuildTree(preorder, inorder);std::cout << tree.GetPostorderString() << std::endl;}return 0;
}

使用智能指针而不使用原始指针构建二叉树

二叉排序树

二叉排序树

题目链接

#include <iostream>
#include <memory>
#include <vector>class BinaryTree {struct Node {  // 二叉树节点Node(){};Node(std::shared_ptr<Node> lchild, std::shared_ptr<Node> rchild,char content): lchild_(lchild), rchild_(rchild), content_(content){};std::shared_ptr<Node> lchild_;std::shared_ptr<Node> rchild_;int content_;};using LinkNode = std::shared_ptr<Node>;public:void BuildTree(const std::vector<int>& arr) {for (int item : arr) {Insert(root_, item);}}void PrintTraversalResult() {  // 输出3种遍历结果PreorderVisit(root_);std::cout << std::endl;InorderVisit(root_);std::cout << std::endl;PostorderVisit(root_);std::cout << std::endl;}private:void Insert(LinkNode& node, int num);void PreorderVisit(LinkNode node);void InorderVisit(LinkNode node);void PostorderVisit(LinkNode node);LinkNode root_;  // 二叉树根节点
};// 输入智能指针的引用,省去返回值。
// LinkNode BinaryTree::Insert(LinkNode node, int num)
void BinaryTree::Insert(LinkNode& node, int num) {if (node == nullptr) {node = std::make_shared<Node>(nullptr, nullptr, num);return;}if (node->content_ > num) {Insert(node->lchild_, num);} else if (node->content_ < num) {Insert(node->rchild_, num);}
}
void BinaryTree::PreorderVisit(LinkNode node) {if (node == nullptr) {return;}std::cout << node->content_ << " ";PreorderVisit(node->lchild_);PreorderVisit(node->rchild_);
}
void BinaryTree::InorderVisit(LinkNode node) {if (node == nullptr) {return;}InorderVisit(node->lchild_);std::cout << node->content_ << " ";InorderVisit(node->rchild_);
}
void BinaryTree::PostorderVisit(LinkNode node) {if (node == nullptr) {return;}PostorderVisit(node->lchild_);PostorderVisit(node->rchild_);std::cout << node->content_ << " ";
}
int main() {int n;while (std::cin >> n) {BinaryTree tree;std::vector<int> arr;arr.resize(n);for (int i = 0; i < n; i++) {std::cin >> arr[i];}tree.BuildTree(arr);tree.PrintTraversalResult();}return 0;
}
  1. 复制粘贴代码记得检查一下:由于几种遍历方式差不多,我直接复制粘贴然后改一下次序,后面debug了很久才发现函数名没改,同时写成了两个lchild_
  2. No rule to make target ‘binary_sort_tree.cpp’, needed by ‘binary_sort_tree’. Stop.
    文件名前有一个空格,导致报错,使用ls命令就能很明显看出

二叉搜索树

题目链接

#include <iostream>
#include <memory>
#include <string>class BinaryTree {struct Node {  // 二叉树节点Node(){};Node(std::shared_ptr<Node> lchild, std::shared_ptr<Node> rchild,char content): lchild_(lchild), rchild_(rchild), content_(content){};std::shared_ptr<Node> lchild_;std::shared_ptr<Node> rchild_;char content_;};using LinkNode = std::shared_ptr<Node>;public:void BuildTree(const std::string& arr) {for (char item : arr) {Insert(root_, item);}GenerateTraversalSequence();  // 生成遍历序列}// 返回可供比较的序列std::string GetCompareSequence() const { return preorder_ + inorder_; }bool Compare(const BinaryTree& tree) {return GetCompareSequence() == tree.GetCompareSequence();}private:void Insert(LinkNode& node, char num);void GenerateTraversalSequence();void PreorderVisit(LinkNode node);void InorderVisit(LinkNode node);void PostorderVisit(LinkNode node);LinkNode root_;  // 二叉树根节点std::string preorder_;std::string inorder_;std::string postorder_;
};// 输入智能指针的引用,省去返回值。
// LinkNode BinaryTree::Insert(LinkNode node, int num)
void BinaryTree::Insert(LinkNode& node, char num) {if (node == nullptr) {node = std::make_shared<Node>(nullptr, nullptr, num);return;}if (node->content_ > num) {Insert(node->lchild_, num);} else if (node->content_ < num) {Insert(node->rchild_, num);}
}
void BinaryTree::PreorderVisit(LinkNode node) {if (node == nullptr) {return;}preorder_.push_back(node->content_);PreorderVisit(node->lchild_);PreorderVisit(node->rchild_);
}
void BinaryTree::InorderVisit(LinkNode node) {if (node == nullptr) {return;}InorderVisit(node->lchild_);inorder_.push_back(node->content_);InorderVisit(node->rchild_);
}
void BinaryTree::PostorderVisit(LinkNode node) {if (node == nullptr) {return;}PostorderVisit(node->lchild_);PostorderVisit(node->rchild_);postorder_.push_back(node->content_);
}
// 只需生成前序与中序序列即可
void BinaryTree::GenerateTraversalSequence() {PreorderVisit(root_);InorderVisit(root_);
}
int main() {int n;std::string seq;while (std::cin >> n && n != 0) {std::cin >> seq;BinaryTree origin_tree;origin_tree.BuildTree(seq);for (int i = 0; i < n; i++) {std::cin >> seq;BinaryTree cmp_tree;cmp_tree.BuildTree(seq);bool ans = origin_tree.Compare(cmp_tree);if (ans) {std::cout << "YES" << std::endl;} else {std::cout << "NO" << std::endl;}}}return 0;
}

注意std::cin与getline函数对换行符的处理的区别

图论一章

并查集

畅通工程

题目链接

#include <iostream>
#include <vector>class UnionFind {public:UnionFind(int n) {size_ = n;tree_.resize(n + 1);for (int i = 1; i <= n; i++) {tree_[i] = i;}}void Union(int p, int q) {int p_root = Find(p);int q_root = Find(q);if (p_root != q_root) {// 只是将第一个值的根节点指向第二个值的根节点,没有进行子树大小判断tree_[p_root] = q_root;size_--;}}int GetSize() { return size_; }private:int Find(int p) {if (tree_[p] == p) {return p;} else {int root = Find(tree_[p]);tree_[p] = root;  // 优化访问路径return root;}}int size_; // 不互通的集合数目(子图什么的?忘了)std::vector<int> tree_;
};int main() {int n, m;while (std::cin >> n && n != 0) {std::cin >> m;UnionFind tool(n);int p, q;for (int i = 0; i < m; i++) {std::cin >> p >> q;tool.Union(p, q);}std::cout << tool.GetSize() - 1 << std::endl;}return 0;
}

这个与之前写的差不多。。。

最小生成树

还是畅通工程

题目链接

#include <algorithm>
#include <iostream>
#include <vector>class UnionFind {public:struct Edge {int from_;int to_;int cost_;};UnionFind(int n, std::vector<Edge>&& edge) : size_(n), sum_(0), edge_(edge) {tree_.resize(n + 1);for (int i = 1; i <= n; i++) {tree_[i] = i;}// 使用lamda表达式自定义排序,权值从小到大std::sort(edge_.begin(), edge_.end(), [](const Edge& e1, const Edge& e2) {return e1.cost_ < e2.cost_;});}void Union(Edge e) {int p_root = Find(e.from_);int q_root = Find(e.to_);if (p_root != q_root) {// 只是将第一个值的根节点指向第二个值的根节点,没有进行子树大小判断tree_[p_root] = q_root;size_--;sum_ += e.cost_;}}int GetMinSpanningTree() {for (const auto& e : edge_) {Union(e);if (size_ == 0) {return sum_;}}return sum_;}private:int Find(int p) {if (tree_[p] == p) {return p;} else {int root = Find(tree_[p]);tree_[p] = root;  // 优化访问路径return root;}}int size_;  // 未连接子图数量int sum_;   // 生成树权值之和std::vector<int> tree_;std::vector<Edge> edge_;
};int main() {int n;while (std::cin >> n && n != 0) {int edge_num = n * (n - 1) / 2;std::vector<UnionFind::Edge> edges;edges.resize(edge_num);for (int i = 0; i < edge_num; i++) {std::cin >> edges[i].from_ >> edges[i].to_ >> edges[i].cost_;}UnionFind tool(n, std::move(edges));std::cout << tool.GetMinSpanningTree() << std::endl;}return 0;
}

我不怎么喜欢在类方法中接受用户输入,故在main方法接受用户输入再传入,有些冗余。

Freckles

题目链接

#include <algorithm>
#include <cmath>
#include <iostream>
#include <vector>class UnionFind {public:struct Dot {double x_;double y_;};struct Edge {int from_;int to_;double cost_;Edge(int from, int to, double cost) : from_(from), to_(to), cost_(cost) {}};UnionFind(int n, const std::vector<Dot>& dots) : size_(n), sum_(0) {tree_.resize(n + 1);for (int i = 1; i <= n; i++) {tree_[i] = i;}edge_.reserve(n * (n - 1) / 2);double cost;  // 计算每条边的代价并加入数组中for (int i = 0; i < n - 1; i++) {for (int j = i + 1; j < n; j++) {cost = GetDistance(dots[i], dots[j]);edge_.emplace_back(Edge{i, j, cost});}}// 使用lamda表达式自定义排序,权值从小到大std::sort(edge_.begin(), edge_.end(), [](const Edge& e1, const Edge& e2) {return e1.cost_ < e2.cost_;});}void Union(Edge e) {int p_root = Find(e.from_);int q_root = Find(e.to_);if (p_root != q_root) {// 只是将第一个值的根节点指向第二个值的根节点,没有进行子树大小判断tree_[p_root] = q_root;size_--;sum_ += e.cost_;}}double GetMinSpanningTree() {for (const auto& e : edge_) {Union(e);if (size_ == 0) {return sum_;}}return sum_;}private:int Find(int p) {if (tree_[p] == p) {return p;} else {int root = Find(tree_[p]);tree_[p] = root;  // 优化访问路径return root;}}double GetDistance(const Dot& lhs, const Dot& rhs) {double res = (lhs.x_ - rhs.x_) * (lhs.x_ - rhs.x_) +(lhs.y_ - rhs.y_) * (lhs.y_ - rhs.y_);return sqrt(res);}int size_;    // 未连接子图数量double sum_;  // 生成树权值之和std::vector<int> tree_;std::vector<Edge> edge_;
};int main() {int n;while (std::cin >> n && n != 0) {std::vector<UnionFind::Dot> dots;dots.resize(n);for (int i = 0; i < n; i++) {std::cin >> dots[i].x_ >> dots[i].y_;}UnionFind tool(n, dots);double weight = tool.GetMinSpanningTree();printf("%.2f", weight);}return 0;
}

全是重复的代码。。。

最短路径

最短路

题目链接 需注册才能提交代码

#include <iostream>
#include <vector>const int kInf = -1;
const int kMax = 1 << 30;
class Solution {public:int GetShortestPath(const std::vector<std::vector<int>>& graph) { return GetShortestPathUseDijkstraForMatrix(graph); }private:struct Edge {int to_;int cost_;Edge(int to, int cost) : to_(to), cost_(cost) {}};int GetShortestPathUseFloyd(const std::vector<std::vector<int>>& graph) {std::vector<std::vector<int>> ans = graph;  // 拷贝邻接矩阵int n = static_cast<int>(ans.size() - 1);for (int k = 1; k <= n; k++) {for (int i = 1; i <= n; i++) {for (int j = 1; j <= n; j++) {if (ans[i][k] == kInf || ans[k][j] == kInf) {continue;}if (ans[i][j] == kInf || ans[i][j] > ans[i][k] + ans[k][j]) {  // 通过节点k到达节点j距离更短ans[i][j] = ans[i][k] + ans[k][j];}}}}return ans[1][n];}int GetShortestPathUseDijkstraForMatrix(const std::vector<std::vector<int>>& graph) {  // 邻接矩阵实现int n = static_cast<int>(graph.size() - 1);std::vector<bool> mark(n + 1, false);std::vector<int> dist(n + 1, kInf);int new_dist;int min_dist;int v = 1;dist[1] = 0;mark[1] = true;for (int times = 0; times <= n; times++) {  // 变量times仅仅控制循环次数,不作数组下标for (int j = 1; j <= n; j++) {            // 第一步:借助节点v刷新dist数组if (mark[j] || graph[v][j] == kInf) {continue;}new_dist = dist[v] + graph[v][j];if (dist[j] == kInf || dist[j] > new_dist) {  // 节点i通过节点v到达节点j距离更短dist[j] = new_dist;}}min_dist = kMax;for (int j = 1; j <= n; j++) {  // 第二步:选择dist数组中未标记的最小值if (mark[j] || dist[j] == kInf) {continue;}if (min_dist > dist[j]) {min_dist = dist[j];v = j;}}mark[v] = true;  // 标记节点v(即加入集合)if (v == n) {    // 找到最短距离,提前结束break;}}return dist[n];}int GetShortestPathUseDijkstraForList(const std::vector<std::vector<int>>& graph) {  // 邻接表实现int n = static_cast<int>(graph.size() - 1);std::vector<std::vector<Edge>> edges;std::vector<bool> mark(n + 1, false);std::vector<int> dist(n + 1, kInf);edges.resize(n + 1);for (int i = 1; i <= n - 1; i++) {  // 由邻接矩阵构建邻接表for (int j = i + 1; j <= n; j++) {if (graph[i][j] != kInf) {edges[i].emplace_back(Edge(j, graph[i][j]));edges[j].emplace_back(Edge(i, graph[i][j]));}}}int new_dist;int min_dist;int edges_size;int to;int cost;int v = 1;dist[1] = 0;mark[1] = true;for (int times = 0; times <= n; times++) {  // 变量times仅仅控制循环次数,不作数组下标edges_size = static_cast<int>(edges[v].size());for (int j = 0; j < edges_size; j++) {  // 第一步:借助节点v刷新dist数组to = edges[v][j].to_;cost = edges[v][j].cost_;if (mark[to]) {continue;}new_dist = dist[v] + cost;if (dist[to] == kInf || dist[to] > new_dist) {  // 节点i通过节点v到达节点j距离更短dist[to] = new_dist;}}min_dist = kMax;for (int j = 1; j <= n; j++) {  // 第二步:选择dist数组中未标记的最小值if (mark[j] || dist[j] == kInf) {continue;}if (min_dist > dist[j]) {min_dist = dist[j];v = j;}}mark[v] = true;  // 标记节点v(即加入集合)if (v == n) {    // 找到最短距离,提前结束break;}}return dist[n];}
};
int main() {Solution solution;int n, m;int p, q, cost;while (std::cin >> n >> m) {if (n == 0 && m == 0) {break;}std::vector<std::vector<int>> graph(n + 1, std::vector<int>(n + 1, kInf));  // n+1 * n+1的二维矩阵for (int i = 0; i < m; i++) {std::cin >> p >> q >> cost;if (graph[p][q] == kInf || graph[p][q] > cost) {  // 可能有重复边,选取最短一条graph[p][q] = cost;graph[q][p] = cost;}}int time = solution.GetShortestPath(graph);std::cout << time << std::endl;}return 0;
}

使用Floyd与Dijkstra算法分别求解最短路径,我之前写的代码中没有考虑重复边,所以是错的。很容易看出代码的执行效率是比较低的,但我主要追求的是好看,也就无所谓了

最短路径问题

题目链接

#include <iostream>
#include <vector>
#include <tuple>const int kInf = -1;
const int kMax = 1 << 30;
class Solution {public:struct Edge {int to_;int dist_;int cost_;Edge(int to, int dist, int cost) : to_(to), dist_(dist), cost_(cost) {}};auto GetShortestPath(const std::vector<std::vector<Edge>>& edges, int start, int end) {int n = static_cast<int>(edges.size() - 1);std::vector<bool> mark(n + 1, false);std::vector<int> dist_matrix(n + 1, kInf);std::vector<int> cost_matrix(n + 1, kInf);int new_dist;int new_cost;int min_dist;int min_cost;int edges_size;int to;int cost;int dist;int v = start;dist_matrix[start] = 0;cost_matrix[start] = 0;mark[start] = true;for (int times = 0; times <= n; times++) {  // 变量times仅仅控制循环次数,不作数组下标edges_size = static_cast<int>(edges[v].size());for (int j = 0; j < edges_size; j++) {  // 第一步:借助节点v刷新dist/cost数组to = edges[v][j].to_;dist = edges[v][j].dist_;cost = edges[v][j].cost_;if (mark[to]) {continue;}new_dist = dist_matrix[v] + dist;new_cost = cost_matrix[v] + cost;if (dist_matrix[to] == kInf || dist_matrix[to] > new_dist || (dist_matrix[to] == new_dist && cost_matrix[to] > new_cost)) {  // 节点i通过节点v到达节点j距离更短dist_matrix[to] = new_dist;cost_matrix[to] = new_cost;}}min_dist = kMax;min_cost = kMax;for (int j = 1; j <= n; j++) {  // 第二步:选择dist/cost数组中未标记的最小值if (mark[j] || dist_matrix[j] == kInf) {continue;}if (min_dist > dist_matrix[j] || (min_dist == dist_matrix[j] && min_cost > cost_matrix[j])) {min_dist = dist_matrix[j];min_cost = cost_matrix[j];v = j;}}mark[v] = true;  // 标记节点v(即加入集合)if (v == end) {  // 找到最短距离,提前结束break;}}return std::make_tuple(dist_matrix[end], cost_matrix[end]);}
};
int main() {Solution solution;int n, m;int p, q, dist, cost;int start, end;std::cin >> n >> m;std::vector<std::vector<Solution::Edge>> edges;edges.resize(n + 1);for (int i = 0; i < m; i++) {std::cin >> p >> q >> dist >> cost;edges[p].emplace_back(Solution::Edge(q, dist, cost));edges[q].emplace_back(Solution::Edge(p, dist, cost));}std::cin >> start >> end;auto [min_dist, min_cost] = solution.GetShortestPath(edges, start, end);std::cout << min_dist << " " << min_cost << std::endl;return 0;
}

距离为一个抽象集合而非特定的变量

拓扑排序

Leagal or Not

没找到相应的oj题,不一定对

#include <iostream>
#include <queue>
#include <vector>class Solution {public:bool IsLeagal(const std::vector<std::vector<int>>& edges, std::vector<int>& indegree) {std::queue<int> zero_indegree_nodes;int n = static_cast<int>(indegree.size());for (int i = 0; i < n; i++) {  // 将入度为0的节点加入队列if (indegree[i] == 0) {zero_indegree_nodes.emplace(i);}}int node_number = 0;int node;int edge_size;int to;while (!zero_indegree_nodes.empty()) {node = zero_indegree_nodes.front();zero_indegree_nodes.pop();node_number++;  // 节点数加一edge_size = static_cast<int>(edges[node].size());for (int i = 0; i < edge_size; i++) {  // 去掉节点,更新入度数组to = edges[node][i];indegree[to]--;if (indegree[to] == 0) {  // 入度变成0,加入队列zero_indegree_nodes.emplace(to);}}}if (node_number == n) {return true;}return false;}
};int main() {Solution solution;int m, n;int x, y;while (std::cin >> n >> m && n != 0) {std::vector<std::vector<int>> edges;edges.resize(n);std::vector<int> indegree(n, 0);for (int i = 0; i < m; i++) {// x->y,我觉得顺序无所谓,即使是y->x对该题来说也没有任何区别,毕竟是判断是否有环std::cin >> x >> y;edges[x].emplace_back(y);indegree[y]++;}bool ans = solution.IsLeagal(edges, indegree);if (ans) {std::cout << "YES" << std::endl;} else {std::cout << "NO" << std::endl;}}
}

动态规划一章

递推求解

N 阶楼梯上楼问题

题目链接

#include <iostream>class Solution {public:// f(n) = f(n-1) + f(n-2)int GetUpstairsWay(int n) {if (n <= 2) {return n;}int f1 = 2;  // n-1时上楼方式数目int f2 = 1;  // n-2时上楼方式数int tmp;for (int i = 3; i < n; i++) {  // 更新f(n-1),f(n-2)tmp = f1;f1 = f1 + f2;f2 = tmp;}return f1 + f2;}
};int main() {Solution solution;int n;int ans;while (std::cin >> n) {ans = solution.GetUpstairsWay(n);std::cout << ans << std::endl;}
}

背包

采药

题目链接

#include <iostream>
#include <vector>class Solution {public:struct Medicine {int time_;int value_;Medicine(int time, int value) : time_(time), value_(value) {}};int GetMaxValue(const std::vector<Medicine>& list, int total_time) {int n = static_cast<int>(list.size());// dp[i][j]表示总时间不超过j的情况下,前i个物品能达到的最大价值// 为节省空间,只需记录dp[j]std::vector<int> dp(total_time + 1, 0);for (int i = 0; i < n; i++) {// 倒序更新dp数组,确保dp[j - list[i].time_]为上一次遍历的结果for (int j = total_time; j >= list[i].time_; j--) {// 状态转移:加入物品i或不加入dp[j] = std::max(dp[j - list[i].time_] + list[i].value_, dp[j]);}}return dp[total_time];}
};int main() {Solution solution;int T, M;int time, value;while (std::cin >> T >> M) {std::vector<Solution::Medicine> list;for (int i = 0; i < M; i++) {std::cin >> time >> value;list.emplace_back(Solution::Medicine(time, value));}int ans = solution.GetMaxValue(list, T);std::cout << ans << std::endl;}
}

Piggy-Bank

没找到相应的题目

#include <iostream>
#include <vector>const int kInf = -1;
class Solution {public:struct Coin {int value_;int weight_;Coin(int value, int weight) : value_(value), weight_(weight) {}};int GetMinAmount(const std::vector<Coin>& coins, int total_weight) {int n = static_cast<int>(coins.size());std::vector<int> dp(total_weight + 1, kInf);  // 初始值不同dp[0] = 0;for (int i = 0; i < n; i++) {for (int j = coins[i].weight_; j <= total_weight; j++) {  // 顺序遍历if (dp[j - coins[i].weight_] != kInf) {if (dp[j] != kInf) {dp[j] = std::min(dp[j - coins[i].weight_] + coins[i].value_, dp[j]);} else {dp[j] = dp[j - coins[i].weight_] + coins[i].value_;}}}}return dp[total_weight];}
};
int main() {Solution solution;int T;int E, F;int N;int P, W;std::cin >> T;while (T--) {std::cin >> E >> F >> N;std::vector<Solution::Coin> coins;coins.reserve(N);for (int i = 0; i < N; i++) {std::cin >> P >> W;coins.emplace_back(Solution::Coin(P, W));}int min_value = solution.GetMinAmount(coins, F - E);if (min_value == kInf) {std::cout << "This is impossible." << std::endl;} else {std::cout << "The minimum amount of money in the piggy-bank is " << min_value << "." << std::endl;}}
}/*
测试数据
3
10 110
2
1 1
30 50
10 110
2
1 1
50 30
1 6
2
10 3
20 4测试结果
The minimum amount of money in the piggy-bank is 60.
The minimum amount of money in the piggy-bank is 100.
This is impossible.
*/

珍惜现在,感恩生活

没找到题目

#include <iostream>
#include <vector>class Solution {public:struct Rice {int price_;int weight_;Rice(int price, int weight) : price_(price), weight_(weight) {}};int GetMaxWeight(const std::vector<Rice>& rices, int money) {int n = static_cast<int>(rices.size());std::vector<int> dp(money + 1, 0);for (int i = 0; i < n; i++) {for (int j = money; j >= rices[i].price_; j--) {dp[j] = std::max(dp[j - rices[i].price_] + rices[i].weight_, dp[j]);}}return dp[money];}
};
int main() {Solution solution;int C;int n, m;int p, h, c;std::cin >> C;while (C--) {std::cin >> n >> m;std::vector<Solution::Rice> rices;rices.reserve(5 * m);  // 预订5*m的空间for (int i = 0; i < m; i++) {std::cin >> p >> h >> c;// 二进制拆分int k = 1;while (c - k > 0) {rices.emplace_back(Solution::Rice(p * k, h * k));c -= k;k *= 2;}if (c != 0) {rices.emplace_back(Solution::Rice(p * c, h * c));}}int max_weight = solution.GetMaxWeight(rices, n);std::cout << max_weight << std::endl;}
}/*
测试数据
2
42 6
10 1 15
19 79 5
6 65 3
8 82 6
16 92 2
17 28 3
8 2
2 100 4
4 100 2测试结果
441
400
*/

二进制拆分相关博客:多重背包 证明二进制拆分n[i]得到的数能够组成0~n[i]

一个数n拆成小于它的所有二的次方的和(指数递增的)加上剩下一个数。实际上就是将这个数的二进制的每一位选或者不选就可以组成一个范围在0~n的数举个例子:13可以拆成 2^0 、 2^1 、 2^2、和 6(6是剩下的那个数),也就是拆分成 1 2 4 6,那么通过选择这4个数的就可以表示0~13的任意数。如:
0:一个都不选。
1:选1;
2:选2;
3:选1和2;
4: 选4;
5:选1和4;
6:选2和4;
7:选1和2和4;
8:选2和6;
9:选1和2和6;
10: 选4和6;
11:选1和4和6;
12:选2和4和6;
13:全选;

王道考研机试指南重写相关推荐

  1. 王道考研机试指南代码合集

    王道考研机试指南代码合集 github链接 王道考研机试指南的代码合集,附有一些笔记和感悟 文件夹中包括机试指南的pdf,笔记部分,和代码对应题目的列表 如发现任何问题欢迎在下面留言 更新: 最短路题 ...

  2. 王道考研机试指南第2版——题目提交链接

    王道考研机试指南第2版--题目链接 文章来自https://github.com/zguolee/WDAlgorithmsNote/blob/master/README.md 王道考研机试指南第2版- ...

  3. 王道出版的机试指南_《王道论坛计算机考研机试指南》试读版.pdf

    <王道论坛计算机考研机试指南>试读版 王道论坛 王道论坛计算机考研机试指南 王道论坛 2013.01.06 写在前面的话 各位王道的小崽子们,今天你们考完初试了,感觉解放了吧?轻松了吧?无 ...

  4. 计算机考研机试指南(八)——数学问题

    机试指南 cha4 数学问题 % 1 #include <iostream> 2 #include <stdio.h> 3 #include <stdlib.h> ...

  5. 【读书笔记】《王道论坛计算机考研机试指南》第五章

    目录 第五章 并查集 最小生成树(MST) 最短路径 拓扑排序 第五章 并查集 本节讨论在图论问题中常常要使用到的一种数据结构一一集合,及其相关操作一一并查集. 我们先来看如下的数字集合: 集合A{1 ...

  6. 王道出版的机试指南_王道论坛计算机考研机试指南 四 排版题

    例2.7 输出梯形 (九度教程第14题) 时间限制:1秒 **内存限制:32兆 ** 特殊判题:否 题目描述: 输入一个高度h,输出一个高为h,上底边为h的梯形. 输入: 一个整数h(1<=h& ...

  7. 考研机试准备--《王道论坛机试指南》学习笔记

    一.代码能力培养的层次结构 1.会编写(默写)经典程序的代码. 2.将自己的想法和抽象的问题转换为代码实现. 3.编写出的代码在大量的,多种多样的测试用例之前仍然具有健壮性. 二.常见概念 1.特殊判 ...

  8. 王道计算机考研机试指南二刷笔记-自用8

    目录 写在开头 第3章 排序与查找 3.1 排序 总结 3.2 查找 总结 写在开头 一刷到后面断更了,发现每题都写太浪费时间了. 一刷总结:数学问题.贪心.递归分治.数据结构二基本都刷完了,图论只刷 ...

  9. 计算机考研机试指南价格,计算机考研:机试指南(第2版)

    目前越来越多的高校开始采用上机考试的形式来考查学生的动手编程能力,而对于以应试为主的大学教学模式,上机往往是学生的薄弱环节.本书由浅入深.从简到难地讲解了机试的相关考点,并精选名校的复试上机真题作为例 ...

  10. 王道计算机考研机试指南刷题笔记-自用

    | 写在开头 前两周磨磨唧唧把第二章暴力求解枚举部分的例题和习题都AC了,思路都不太难,找到规律很快就能写出来,想着一定要写刷题笔记,把coding时遇到的困难都记录下来,方便以后复习,却也一直未曾动 ...

最新文章

  1. LeetCode简单题之猜数字大小
  2. R语言ggplot2可视化使用ggplotly(plotly version of R)可视化散点图(scatter plot)
  3. mongodb 监控项详解(mms)
  4. VS2017 error C3646: 未知重写说明符
  5. 1 个闭环 + 1 个案例,为你解读实现数据驱动的秘诀
  6. aws终止实例后还收费吗_因 AWS 数据中心断电,1TB 数据丢失了
  7. CVPR 2019 | APDrawingGAN:人脸秒变艺术肖像画
  8. Spring中的Bean的生命周期
  9. K-Backup备份office文件的具体操作。
  10. Notice: Undefined variable: id in D:\phpStudy18\PHPTutorial\WWW\sqli-labs-master\Less-32\index.php o
  11. 决策树算法绘图——graphviz安装
  12. map函数python求数的每位的值_map函数_实现批量计算
  13. 1941套站点模版,终生收藏,个个精品
  14. media encoder中文2022已上线
  15. 长沙理工大学第十二届ACM大赛L 选择困难症 (剪枝暴搜)
  16. 学python需要c-为什么我建议每个开发人员都需要学 Python ?
  17. cerebro安装部署
  18. springboot中使用poi-tl导出word(包含表格合并单元格)实例
  19. C++ 左值引用和右值引用
  20. DDP及其在pytorch中应用

热门文章

  1. Linux下软件管理方式
  2. java path环境变量_java配置环境变量
  3. DSP28335定时器简易秒表设计
  4. 03.【Axure 10 】win系统软件汉化
  5. visio 2020 最新版安装过程及注意事项
  6. Tableau安装与破解
  7. 简单的SQL数据脱敏
  8. python冰雪奇缘使用教程_99 行 Python 代码实现《冰雪奇缘》特效?网友:大神碉堡!...
  9. js制作随机抽奖,(指定数字范围内随机出现一个数字)
  10. python 随机选择数字