考试必备
既然有4小时且不确定考状压,我们需要做更全面的准备。初中生参加NOIP级别的测试,题目通常覆盖动态规划、图论、搜索、数据结构等核心算法。
📊 各算法押题概率分析(5题分布预测)
1. 动态规划综合题(95%)
- 线性DP/区间DP:最长公共子序列、石子合并等
- 背包问题变种:多重背包优化、分组背包
- 树形DP:树上的最大独立集、直径等
- 状压DP:n≤20的集合选择问题
2. 图论基础题(90%)
- 最短路:Dijkstra(优先队列优化)
- 最小生成树:Kruskal或Prim
- 拓扑排序:任务安排、课程表类问题
3. 数据结构应用(80%)
- 并查集:带权并查集、扩展域
- 单调栈/队列:滑动窗口最值
- 树状数组:区间求和、逆序对
4. 搜索与剪枝(70%)
- DFS剪枝:可行性剪枝、最优性剪枝
- BFS状态搜索:八数码、迷宫类
- 记忆化搜索:DP的递归实现
5. 数学/思维题(60%)
- 组合数学:排列组合、容斥原理
- 贪心策略:区间调度、哈夫曼编码
- 简单数论:GCD、快速幂
🚀 4小时考场最佳策略
时间分配(黄金比例)
0-15分钟:浏览所有题目,评估难度
15-60分钟:完成最熟悉的1-2题(建立信心)
60-180分钟:主攻中等难度题(仔细实现)
180-210分钟:挑战难题部分分
210-240分钟:全面检查(边界、极端数据)
做题顺序建议
- 签到题(15分钟内能AC的)
- 套路题(熟悉模板的,如图论、DP)
- 中等题(需要思考但可实现的)
- 难题(写暴力拿部分分)
- 检查所有题(输入输出、数组大小)
万能代码库(直接使用)
1. 输入输出优化(必带)
#include <bits/stdc++.h>
using namespace std;
// 快读
inline int read() {
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-') f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = x * 10 + ch - '0';
ch = getchar();
}
return x * f;
}
// 快写
inline void write(int x) {
if (x < 0) {
putchar('-');
x = -x;
}
if (x > 9) write(x / 10);
putchar(x % 10 + '0');
}
2. 动态规划(DP)
2.1 最长上升子序列(LIS) - O(n log n)
int LIS(vector<int>& a) {
int n = a.size();
vector<int> d; // 存储当前找到的上升子序列
for (int i = 0; i < n; i++) {
// 找到第一个大于等于a[i]的位置
auto it = lower_bound(d.begin(), d.end(), a[i]);
if (it == d.end())
d.push_back(a[i]); // 如果a[i]比所有都大,则扩展
else
*it = a[i]; // 否则替换,使得序列尽可能小
}
return d.size();
}
2.2 最长公共子序列(LCS) - O(nm)
int LCS(string& a, string& b) {
int n = a.size(), m = b.size();
vector<vector<int>> dp(n+1, vector<int>(m+1, 0));
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (a[i-1] == b[j-1])
dp[i][j] = dp[i-1][j-1] + 1;
else
dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
}
}
return dp[n][m];
}
2.3 背包问题
01背包(滚动数组)
int knapsack01(vector<int>& w, vector<int>& v, int capacity) {
int n = w.size();
vector<int> dp(capacity+1, 0);
for (int i = 0; i < n; i++) {
for (int j = capacity; j >= w[i]; j--) {
dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
}
}
return dp[capacity];
}
完全背包
int completeKnapsack(vector<int>& w, vector<int>& v, int capacity) {
int n = w.size();
vector<int> dp(capacity+1, 0);
for (int i = 0; i < n; i++) {
for (int j = w[i]; j <= capacity; j++) {
dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
}
}
return dp[capacity];
}
2.4 区间DP(石子合并)
int stoneMerge(vector<int>& stones) {
int n = stones.size();
vector<int> prefix(n+1, 0);
for (int i = 0; i < n; i++) prefix[i+1] = prefix[i] + stones[i];
vector<vector<int>> dp(n, vector<int>(n, INT_MAX));
for (int i = 0; i < n; i++) dp[i][i] = 0;
for (int len = 2; len <= n; len++) {
for (int l = 0; l + len - 1 < n; l++) {
int r = l + len - 1;
for (int k = l; k < r; k++) {
dp[l][r] = min(dp[l][r], dp[l][k] + dp[k+1][r] + prefix[r+1] - prefix[l]);
}
}
}
return dp[0][n-1];
}
3. 图论
3.1 最短路径
Dijkstra(优先队列优化) - O((V+E) log V)
const int INF = 0x3f3f3f3f;
void dijkstra(int s, vector<vector<pair<int, int>>>& graph, vector<int>& dist) {
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<>> pq;
dist[s] = 0;
pq.push({0, s});
while (!pq.empty()) {
auto [d, u] = pq.top(); pq.pop();
if (d > dist[u]) continue;
for (auto& [v, w] : graph[u]) {
if (dist[v] > dist[u] + w) {
dist[v] = dist[u] + w;
pq.push({dist[v], v});
}
}
}
}
Bellman-Ford(判断负环)
struct Edge {
int u, v, w;
};
bool bellmanFord(int n, vector<Edge>& edges, int s, vector<int>& dist) {
dist.assign(n, INF);
dist[s] = 0;
for (int i = 0; i < n - 1; i++) {
for (auto& e : edges) {
if (dist[e.u] != INF && dist[e.v] > dist[e.u] + e.w) {
dist[e.v] = dist[e.u] + e.w;
}
}
}
// 检查负环
for (auto& e : edges) {
if (dist[e.u] != INF && dist[e.v] > dist[e.u] + e.w) {
return false; // 有负环
}
}
return true;
}
Floyd(多源最短路) - O(n^3)
void floyd(vector<vector<int>>& dist, int n) {
for (int k = 0; k < n; k++) {
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (dist[i][k] != INF && dist[k][j] != INF)
dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);
}
}
}
}
3.2 最小生成树
Kruskal
struct Edge {
int u, v, w;
bool operator<(const Edge& other) const { return w < other.w; }
};
int find(vector<int>& parent, int x) {
return parent[x] == x ? x : parent[x] = find(parent, parent[x]);
}
int kruskal(vector<Edge>& edges, int n) {
sort(edges.begin(), edges.end());
vector<int> parent(n);
for (int i = 0; i < n; i++) parent[i] = i;
int total = 0, cnt = 0;
for (auto& e : edges) {
int pu = find(parent, e.u), pv = find(parent, e.v);
if (pu != pv) {
parent[pu] = pv;
total += e.w;
if (++cnt == n - 1) break;
}
}
return cnt == n - 1 ? total : -1; // 如果不连通返回-1
}
3.3 拓扑排序
vector<int> topologicalSort(int n, vector<vector<int>>& graph, vector<int>& indegree) {
queue<int> q;
for (int i = 0; i < n; i++) {
if (indegree[i] == 0) q.push(i);
}
vector<int> order;
while (!q.empty()) {
int u = q.front(); q.pop();
order.push_back(u);
for (int v : graph[u]) {
if (--indegree[v] == 0) {
q.push(v);
}
}
}
if (order.size() != n) return {}; // 有环
return order;
}
3.4 强连通分量(Tarjan)
void tarjan(int u, vector<vector<int>>& graph, vector<int>& dfn, vector<int>& low,
vector<bool>& inStack, stack<int>& st, int& index, vector<vector<int>>& scc) {
dfn[u] = low[u] = ++index;
st.push(u);
inStack[u] = true;
for (int v : graph[u]) {
if (!dfn[v]) {
tarjan(v, graph, dfn, low, inStack, st, index, scc);
low[u] = min(low[u], low[v]);
} else if (inStack[v]) {
low[u] = min(low[u], dfn[v]);
}
}
if (dfn[u] == low[u]) {
vector<int> component;
int v;
do {
v = st.top(); st.pop();
inStack[v] = false;
component.push_back(v);
} while (v != u);
scc.push_back(component);
}
}
4. 数据结构
4.1 并查集(DSU)
class DSU {
vector<int> parent, rank;
public:
DSU(int n) : parent(n), rank(n, 0) {
for (int i = 0; i < n; i++) parent[i] = i;
}
int find(int x) {
return parent[x] == x ? x : parent[x] = find(parent[x]);
}
void unite(int x, int y) {
x = find(x), y = find(y);
if (x == y) return;
if (rank[x] < rank[y]) parent[x] = y;
else {
parent[y] = x;
if (rank[x] == rank[y]) rank[x]++;
}
}
bool same(int x, int y) { return find(x) == find(y); }
};
4.2 树状数组(Fenwick Tree)
class Fenwick {
vector<int> tree;
int n;
public:
Fenwick(int size) : n(size), tree(size + 1, 0) {}
void update(int idx, int delta) {
for (; idx <= n; idx += idx & -idx) tree[idx] += delta;
}
int query(int idx) {
int sum = 0;
for (; idx > 0; idx -= idx & -idx) sum += tree[idx];
return sum;
}
int rangeSum(int l, int r) { return query(r) - query(l - 1); }
};
4.3 线段树(区间求和、区间更新)
class SegmentTree {
vector<int> tree, lazy;
int n;
void build(vector<int>& arr, int node, int start, int end) {
if (start == end) {
tree[node] = arr[start];
} else {
int mid = (start + end) / 2;
build(arr, node*2, start, mid);
build(arr, node*2+1, mid+1, end);
tree[node] = tree[node*2] + tree[node*2+1];
}
}
void pushDown(int node, int start, int end) {
if (lazy[node]) {
int mid = (start + end) / 2;
tree[node*2] += lazy[node] * (mid - start + 1);
tree[node*2+1] += lazy[node] * (end - mid);
lazy[node*2] += lazy[node];
lazy[node*2+1] += lazy[node];
lazy[node] = 0;
}
}
void updateRange(int node, int start, int end, int l, int r, int val) {
if (l > end || r < start) return;
if (l <= start && end <= r) {
tree[node] += val * (end - start + 1);
lazy[node] += val;
return;
}
pushDown(node, start, end);
int mid = (start + end) / 2;
updateRange(node*2, start, mid, l, r, val);
updateRange(node*2+1, mid+1, end, l, r, val);
tree[node] = tree[node*2] + tree[node*2+1];
}
int queryRange(int node, int start, int end, int l, int r) {
if (l > end || r < start) return 0;
if (l <= start && end <= r) return tree[node];
pushDown(node, start, end);
int mid = (start + end) / 2;
return queryRange(node*2, start, mid, l, r) +
queryRange(node*2+1, mid+1, end, l, r);
}
public:
SegmentTree(vector<int>& arr) {
n = arr.size();
tree.resize(4*n);
lazy.resize(4*n, 0);
build(arr, 1, 0, n-1);
}
void update(int l, int r, int val) { updateRange(1, 0, n-1, l, r, val); }
int query(int l, int r) { return queryRange(1, 0, n-1, l, r); }
};
5. 数学
5.1 快速幂
long long quickPow(long long a, long long b, long long mod) {
long long res = 1;
while (b) {
if (b & 1) res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res;
}
5.2 素数判定(Miller-Rabin)
using ll = long long;
ll mul_mod(ll a, ll b, ll mod) {
return (__int128)a * b % mod;
}
ll pow_mod(ll a, ll b, ll mod) {
ll res = 1;
while (b) {
if (b & 1) res = mul_mod(res, a, mod);
a = mul_mod(a, a, mod);
b >>= 1;
}
return res;
}
bool millerRabin(ll n) {
if (n < 3 || n % 2 == 0) return n == 2;
ll a = n - 1, b = 0;
while (a % 2 == 0) a /= 2, ++b;
// 测试的底数,可以足够判断2^64内的素数
vector<ll> test_nums = {2, 325, 9375, 28178, 450775, 9780504, 1795265022};
for (ll x : test_nums) {
if (x % n == 0) continue;
ll v = pow_mod(x, a, n);
if (v == 1 || v == n - 1) continue;
int j;
for (j = 0; j < b; ++j) {
v = mul_mod(v, v, n);
if (v == n - 1) break;
}
if (j >= b) return false;
}
return true;
}
5.3 最大公约数(GCD)和最小公倍数(LCM)
int gcd(int a, int b) { return b == 0 ? a : gcd(b, a % b); }
int lcm(int a, int b) { return a / gcd(a, b) * b; }
5.4 扩展欧几里得(解 ax + by = gcd(a,b))
int exgcd(int a, int b, int &x, int &y) {
if (b == 0) {
x = 1, y = 0;
return a;
}
int d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
6. 字符串
6.1 KMP(字符串匹配)
vector<int> getNext(string& pattern) {
int n = pattern.size();
vector<int> next(n, 0);
for (int i = 1, j = 0; i < n; i++) {
while (j > 0 && pattern[i] != pattern[j]) j = next[j-1];
if (pattern[i] == pattern[j]) j++;
next[i] = j;
}
return next;
}
vector<int> kmp(string& text, string& pattern) {
vector<int> next = getNext(pattern);
vector<int> positions;
int n = text.size(), m = pattern.size();
for (int i = 0, j = 0; i < n; i++) {
while (j > 0 && text[i] != pattern[j]) j = next[j-1];
if (text[i] == pattern[j]) j++;
if (j == m) {
positions.push_back(i - m + 1);
j = next[j-1];
}
}
return positions;
}
7. 其他
7.1 离散化
vector<int> discretize(vector<int>& a) {
vector<int> b = a;
sort(b.begin(), b.end());
b.erase(unique(b.begin(), b.end()), b.end());
for (int& x : a) {
x = lower_bound(b.begin(), b.end(), x) - b.begin() + 1; // 从1开始
}
return b; // 返回原始值
}
🎯 最后时刻提醒清单
考前准备
- 打印常用模板(如果允许)
- 带好准考证、身份证、笔、水
- 穿舒适的衣服(考场可能冷)
考试中
- 先读所有题目,标记难度
- 每道题先估算最坏时间复杂度
- 大数组开全局,避免栈溢出
- 使用long long防溢出(尤其是乘法)
- 检查边界:n=0, n=1, n=最大值
调试技巧
// 调试宏(考完记得注释掉)
#define DEBUG
#ifdef DEBUG
#define debug(x) cout << #x << " = " << x << endl
#else
#define debug(x)
#endif
// 打印数组
void printArray(int arr[], int n) {
for(int i=0; i<n; i++) cout << arr[i] << " ";
cout << endl;
}
时间紧急的应对策略
if (剩余时间<30分钟):
1. 确保所有题都有代码(即使暴力)
2. 检查输入输出格式
3. 测试样例是否通过
4. 注释思路争取部分分
if (遇到难题卡住):
1. 写朴素算法(n≤10的暴力)
2. 输出特殊情况的解
3. 猜结论写贪心
💪 心态调整
- 选拔看的是相对表现,不是绝对分数
- 遇到难题正常,大家都难
- 部分分很重要,争取每一分
- 最后15分钟不要写新代码,专心检查
记住:4小时很长,足够你展现所有能力。先稳后快,确保简单题全对,中等题多拿分,难题有尝试。
**晚上,明天正常发挥就是胜利!加油!

浙公网安备 33010602011771号