ARTICLE · 1228977
CSP-J/S 2026 :图论,模板打熟就是白送的分
10 月 31 日(周六)上午 8:30,CSP-J/S 2026 第二轮开考。
一、考到哪,先划边界
CSP-J 的图论基本只考四样:最短路(Dijkstra / Floyd)、并查集、最小生成树、拓扑排序,规模通常是 n ≤ 1000、m ≤ 10⁵,无负边权。CSP-S 再加负边权(SPFA)、二分图染色、树上差分。
九天内,J 组选手只需把前四个模板打到能默写,S 组选手加一个 SPFA。其余的等考完再说。
二、存图:别用链式前向星
链式前向星四个数组又长又易错,考场上用 `vector` 邻接表就够,对 10⁵ 完全不慢:
cpp
struct Edge { int to, w; };
vector<Edge> g[N];
void add(int u, int v, int w) {
g[u].push_back({v, w});
g[v].push_back({u, w}); // 无向图才加这行
}无向图记得加反向边,有向图一定不要加——这是图论题最常见的第一个 WA。
三、Dijkstra:复赛最常考的一个
单源最短路,边权非负。核心:每次取出当前距离最小的点,用它松弛邻居。
cpp
const long long INF = 1e18;
long long dis[N]; bool vis[N];
void dijkstra(int s, int n) {
for (int i = 1; i <= n; i++) dis[i] = INF, vis[i] = false;
priority_queue<pair<long long,int>,
vector<pair<long long,int>>, greater<>> pq;
dis[s] = 0; pq.push({0, s});
while (!pq.empty()) {
auto [d, u] = pq.top(); pq.pop();
if (vis[u]) continue; // 关键:弹出时判
vis[u] = true;
for (auto e : g[u])
if (dis[e.to] > d + e.w) {
dis[e.to] = d + e.w;
pq.push({dis[e.to], e.to});
}
}
}四个必踩的坑:
0x3f3f3f3f 加两次就溢出成负数。greater 才按距离排序,写成 {点, 距离} 直接错。四、Floyd:点少时的万能解
多源最短路,n ≤ 300 直接上:
cpp
for (int k = 1; k <= n; k++)
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
if (f[i][k] + f[k][j] < f[i][j])
f[i][j] = f[i][k] + f[k][j];`k` 必须在最外层——唯一不能动的地方,写错顺序样例都常常能过。初始化 f[i][i] = 0、其余 INF,`f[i][i]=0` 忘写,答案会莫名偏大。
有负边权才换 SPFA,判负环加个入队计数、超过 n 次即有环。CSP-J 几乎不考;S 组无负边且 m 大时直接 Dijkstra,别赌 SPFA 不被卡。
五、并查集:两行,错一行全错
cpp
int fa[N];
int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }
void merge(int a, int b) { fa[find(a)] = find(b); }fa[x] = find(fa[x]) 里的赋值是路径压缩,漏掉复杂度退化成 O(n),10⁵ 次操作直接超时。初始化必须 for (int i = 1; i <= n; i++) fa[i] = i;,忘了初始化,所有点全指向 0。
六、最小生成树:Kruskal = 排序 + 并查集
按边权从小到大试,能连就连,用并查集判环:
cpp
sort(e + 1, e + m + 1, [](Edge a, Edge b) { return a.w < b.w; });
int cnt = 0; long long ans = 0;
for (int i = 1; i <= m; i++) {
if (find(e[i].u) == find(e[i].v)) continue; // 已连通,加边成环
merge(e[i].u, e[i].v);
ans += e[i].w;
if (++cnt == n - 1) break;
}
if (cnt < n - 1) puts("-1"); // 图不连通判据一句话:选满 n-1 条边才是树,选不满就是图不连通。最后这个 if 是很多题的第二个得分点。
七、拓扑排序:顺便判有环
统计入度,入度为 0 的入队,出队时把邻居入度减一:
cpp
for (int i = 1; i <= n; i++) if (!in[i]) q.push(i);
while (!q.empty()) {
int u = q.front(); q.pop(); ans.push_back(u);
for (int v : g[u]) if (--in[v] == 0) q.push(v);
}
if ((int)ans.size() < n) puts("有环"); // 有环就排不完判环只看最后排出的点数是否为 n。"任务调度""课程表"类题反复考这一招。
八、图论题的五个通用坑
g[i].clear() 漏了,第二组带着第一组的边跑。u == v。INF + INF 必须仍在 long long 内,所以用 1e18 而不是 LLONG_MAX。九、今天 30 分钟
vis 位置和 long long。find,确认有 fa[x] = find(fa[x])。本期小结
dis 用 long long、INF=1e18、vis 弹出时判k 必须在最外层,f[i][i]=0 不能忘有问题在评论区留言,吕老师每天回复。















































































