1 条题解
-
0
一、题意转化
给定 个点、 条边的无向简单图(无自环、无重边)。
对无序点对 (),它是“间接朋友”当且仅当同时满足:
- 不相邻(不是直接朋友);
- 至少有一个公共邻居 (即存在 使 、 都是边)。
所以本题等价于:统计图中“最短距离恰好为 2”的点对个数。
注意条件 2 是“存在性”——有多个公共邻居也只算一对。
二、思路分析
观察 1:,邻接矩阵完全够用
判断“两点是否相邻”需要 查询,用
adj[u][v]最合适。空间 仅约 250 KB,用char/bool存更省。观察 2:直接按定义枚举即可
枚举所有 (共 对),对每一对:
- 若
adj[i][j]==1→ 直接朋友,跳过; - 否则枚举 ,只要发现
adj[i][k] && adj[j][k],答案加一并立刻 break。
最坏时间 次基本操作,C++ 约 0.1~0.3 s,能过。稀疏图上因为
break提前退出,实际远快于上界。观察 3:不能“数公共邻居个数再除 2”
有人会想到用邻接矩阵平方 ,其中 是 的公共邻居数量。但题目要求的是“是否存在”,不是“有多少条长度为 2 的路径”。所以判 即可,千万不要把 累加进答案。
观察 4:进阶写法(可选)
把每一行压成
bitset,公共邻居存在性 =(b[i] & b[j]).any(),一次位运算处理 64 位,复杂度降到 , 到几千也能过。
三、算法步骤
- 读入 ;初始化 的邻接矩阵为 0;
- 读入 条边,令
adj[u][v]=adj[v][u]=1( 时此步不执行,程序仍正常输出 0); ans = 0,双重循环 :if (adj[i][j]) continue;- 内层循环 ,若
adj[i][k] && adj[j][k]则ans++, break;
- 输出
ans。
四、复杂度
项目 值 时间 ,最坏约 ;bitset 版 空间 ,约 250 KB( char版)答案上界 , int够用,代码统一用long long
五、易错点
# 易错点 处理 1 时输入只有第一行 正常读入即可,循环 0 次,输出 0 2 多个公共邻居被重复统计 找到第一个就 break3 把直接朋友也算进去 先判 adj[i][j]并continue4 只枚举 否则答案翻倍 5 误把公共邻居个数累加 本题只判存在性,计数要 +1而非+=个数6 距离为 3 的点被误算 距离为 3 意味着无公共邻居,自然不会被计入 7 图不连通 不影响,算法天然只认“公共邻居”
六、参考代码
版本一:邻接矩阵暴力(推荐,首选提交)
#include <bits/stdc++.h> using namespace std; /* * 题意:给定 N 个点 M 条边的无向简单图,求无序点对 (A,B) 的个数, * 满足 A、B 之间没有边,且至少存在一个公共邻居 C。 * * 做法:邻接矩阵 + 三重循环枚举 (i, j, k)。 * 复杂度:时间 O(N^3)(找到公共邻居即 break),空间 O(N^2)。 */ int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n, m; if (!(cin >> n >> m)) return 0; // 处理空输入 // adj[u][v] == 1 表示 u、v 是直接朋友;用 char 省空间 vector<vector<char>> adj(n + 1, vector<char>(n + 1, 0)); for (int i = 0; i < m; i++) { int u, v; cin >> u >> v; adj[u][v] = adj[v][u] = 1; } long long ans = 0; for (int i = 1; i <= n; i++) { for (int j = i + 1; j <= n; j++) { if (adj[i][j]) continue; // 直接朋友不计入 for (int k = 1; k <= n; k++) { if (adj[i][k] && adj[j][k]) { // 存在公共邻居 k ans++; break; // 只算一次,立即跳出 } } } } cout << ans << '\n'; return 0; }版本二:bitset 加速(常数更小,数据加强时备用)
#include <bits/stdc++.h> using namespace std; const int MAXN = 505; /* * 做法:把每个点的邻居压成 bitset, * (b[i] & b[j]).any() 即表示 i、j 有公共邻居。 * 复杂度:时间 O(N^3 / 64),空间 O(N^2 / 8)。 */ int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n, m; if (!(cin >> n >> m)) return 0; vector<bitset<MAXN>> b(n + 1); for (int i = 0; i < m; i++) { int u, v; cin >> u >> v; b[u].set(v); b[v].set(u); } long long ans = 0; for (int i = 1; i <= n; i++) { for (int j = i + 1; j <= n; j++) { if (!b[i].test(j) && (b[i] & b[j]).any()) // 无边 & 交集非空 ans++; } } cout << ans << '\n'; return 0; }
七、样例与自测
官方样例
输入: 4 4 1 2 2 3 3 4 1 4 输出: 2说明:间接朋友为 (公共邻居 2、4)和 (公共邻居 1、3),各只算一次。
手算验证用例
3 0 → 0 (无边) 3 2 → 1 (链 1-2-3,只有 (1,3)) 1 2 2 3 4 3 → 3 (星形图,叶子两两配对) 1 2 1 3 1 4 5 4 → 3 (链 1-2-3-4-5:(1,3)(2,4)(3,5)) 1 2 2 3 3 4 4 5 4 6 → 4 (完全图 K4 补 0 对,再加孤立点也不变) 1 2 1 3 1 4 2 3 2 4 3 4
- 1