无向图三元环 查找/计数
理解
时间复杂度 \(O(M \sqrt{M})\)
作用
求出无向图的所有三元环
过程
首先要对所有的无向边进行定向,对于任何一条边,从度数大的点连向度数小的点,如果度数相同,从编号小的点连向编号大的点。
此时这张图是一个有向无环图。
- 枚举每一个点u,然后将u的所有相邻的点都标记上“被u访问了”,
- 然后再枚举u的相邻的点v,
- 然后再枚举v的相邻的点w,
如果w存在“被u访问了”的标记,那么(u,v,w)就是一个三元环了,且每个三元环只会被计算一次。
这样为什么是对的呢?
按照算法的流程,原来图中的一个三元环 \(((i,j,k)\) 在重定向之后一定变成了 i 向 j,k 连边,j,k 之间再连一条边的情况,不妨设为\(j \to k\) 。
我们枚举 i 时会标记 j,k ,再遍历 j找第三个点的时候一定会找到 k 。
并且对于这个环,遍历顺序只可能是 \(i\to j \to k\),也就是我们只能由 i 来找到这个环。每个点我们只会枚举一次,
所以图中的每一个三元环,我们都会遍历 且 仅遍历一次。
那么时间复杂度为什么 \(O(m\sqrt m)\) 呢?
考虑每条边 \((u,v)\) 对时间复杂度的贡献与 \(v\) 的出度同阶,总的时间复杂度应当是 $\sum_{i=1}^m d_v$,这里 \(d\) 是出度。
对于原图中度数不大于 \(\sqrt{m}\) 的点,新图中的度数也不可能更大,所以上界 \(\sqrt{m}\)。
对于原图中度数大于 \(\sqrt{m}\) 的点,由于我们只向度数大的点连边,而度数大于 \(\sqrt{m}\) 的点最多有 \(\sqrt{m}\)个,度数也不可能超过 \(\sqrt m\)。
综上,总的时间复杂度 \(O(m\sqrt{m})\)。
模板代码1
时间复杂度 \(O(M \sqrt{M})\)
将边先存起来,然后再建图。
优点: 代码清晰,好看
缺点: 空间大些
int n,m;
vector<int> g[N];
vector<pii> vec;
int d[N];
int vis[N];
int get_sum(){
int ans=0;
for(int i=1;i<=n;i++){
for(int j: g[i]) vis[j]=1;
for(int j:g[i])
for(int k:g[j])
if(vis[k]) ans++;
for(int j: g[i]) vis[j]=0;
}
return ans;
}
void solve(){
cin>>n>>m;
for(int i=0;i<m;i++){
int u,v;cin>>u>>v;
d[u]++,d[v]++;
vec.push_back({u,v});
}
for(pii t: vec){
int u=t.first,v=t.second;
if(d[u]>d[v] ||(d[u]==d[v] && u>v)) swap(u,v);
g[u].push_back(v);
}
cout<<get_sum()<<endl;
}
模板代码2
时间复杂度 \(O(M \sqrt{M})\)
直接建图,然后通过判断限制,达到单向的目的。
优点: 空间优秀
缺点: 代码难看
int n, m;
vector<int> g[N];
int vis[N];
int get_sum()
{
int ans=0;
for (int i = 1; i <= n; i++)
{
for (int j : g[i])
if(g[i].size()>g[j].size() || (g[i].size()==g[j].size() && i>j))
vis[j] = 1;
for (int j : g[i])
if(g[i].size()>g[j].size() || (g[i].size()==g[j].size() && i>j))
for (int k : g[j])
if(g[j].size()>g[k].size() || (g[j].size()==g[k].size() && j>k))
if (vis[k])
ans++;
for (int j : g[i])
if(g[i].size()>g[j].size() || (g[i].size()==g[j].size() && i>j))
vis[j] = 0;
}
return ans;
}
void solve(){
cin>>n>>m;
for(int i=0;i<m;i++){
int u,v;cin>>u>>v;
g[u].push_back(v);
g[v].push_back(u);
}
cout<<get_sum()<<endl;
}
模板题
P1989 无向图三元环计数
代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define pii pair<int,int>
const int N=1e5+10;
const int mod=1e9+7;
int n,m;
vector<int> g[N];
vector<pii> vec;
int d[N];
int vis[N];
int get_sum(){
int ans=0;
for(int i=1;i<=n;i++){
for(int j: g[i]) vis[j]=1;
for(int j:g[i])
for(int k:g[j])
if(vis[k]) ans++;
for(int j: g[i]) vis[j]=0;
}
return ans;
}
void solve(){
cin>>n>>m;
for(int i=0;i<m;i++){
int u,v;cin>>u>>v;
d[u]++,d[v]++;
vec.push_back({u,v});
}
for(pii t: vec){
int u=t.first,v=t.second;
if(d[u]>d[v] ||(d[u]==d[v] && u>v)) swap(u,v);
g[u].push_back(v);
}
cout<<get_sum()<<endl;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t=1;
// cin>>t;
while(t--)
solve();
return 0;
}
C - Friend-Graph HDU - 6152
注意及时剪枝结束,不然会超时。
代码
#include <bits/stdc++.h>
using namespace std;
// #define int long long
// #define pii pair<int, int>
//***********************必不可少,不然内存超限
// #define int short int
const int N = 3010;
const int mod = 1e9 + 7;
int n, m;
vector<int> g[N];
int vis[N];
int get_sum()
{
for (int i = 1; i <= n; i++)
{
for (int j : g[i])
if(g[i].size()>g[j].size() || (g[i].size()==g[j].size() && i>j))
vis[j] = 1;
for (int j : g[i])
if(g[i].size()>g[j].size() || (g[i].size()==g[j].size() && i>j))
for (int k : g[j])
if(g[j].size()>g[k].size() || (g[j].size()==g[k].size() && j>k))
if (vis[k])
return 1;
for (int j : g[i])
if(g[i].size()>g[j].size() || (g[i].size()==g[j].size() && i>j))
vis[j] = 0;
}
return 0;
}
void init()
{
m = 0;
for (int i = 1; i <= n; ++i)
g[i].clear();
}
void solve()
{
cin >> n;
init();
for (int i = 1; i <= n - 1; i++){
for (int j = i + 1; j <= n; j++){
int t;cin >> t;
if (t){
m++;
g[i].push_back(j);
g[j].push_back(i);
}
}
}
int ans1 = 0, ans2 = 0;
ans1 = get_sum();
m = n * (n - 1) / 2 - m;
//反向建边
for (int i = 1; i <= n; ++i)
{
memset(vis, 0, sizeof vis);
for (int j : g[i])
{
vis[j] = 1;
}
g[i].clear();
for (int j = 1; j <= n; j++)
{
if (vis[j] == 0)
g[i].push_back(j);
}
}
ans2 = get_sum();
if (ans1 == 0 && ans2 == 0)
cout << "Great Team!" << endl;
else
cout << "Bad Team!" << endl;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t = 1;
cin >> t;
while (t--)
solve();
return 0;
}
原文1:https://blog.51cto.com/u_15072778/3772333
原文2:https://www.luogu.com.cn/blog/i207M/san-yuan-huan-ji-shuo-xue-xi-bi-ji