IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 非标定的n阶无向图的非同构个数 -> 正文阅读

[数据结构与算法]非标定的n阶无向图的非同构个数

非标定的n阶无向图的非同构个数

特别鸣谢wp师兄跟我的讲解

问题分析:

1. 先不考虑同构,只管标定图的数量

首先我们先从标定图开始看,总共有多少个n阶标定图?

其实这就相当于固定了点数,数边的数量。共有 n ( n ? 1 ) / 2 n(n-1)/2 n(n?1)/2 条边,那么总共就有 2 n ( n ? 1 ) 2 2^{\frac{n(n-1)}{2}} 22n(n?1)?个标定图(最后发现这个数量并没有用

2. 考虑同构,并将这些标定图进行分组

接下来我们来将这些图分类,我们不妨设共有 t t t 个不同构的图记作 G 1 , G 2 , . . . , G t G_1,G_2,...,G_t G1?,G2?,...,Gt?,然后我们再设一个集合,取出所有标定图中与其同构的图,共同放入这个集合中,记作 S { G i } S\{G_i\} S{Gi?}

S { G 1 } = { G 11 , G 12 , G 13 , . . . , G 1 k 1 } S\{G_1\}=\{G_{11},G_{12},G_{13},...,G_{1k_1}\} S{G1?}={G11?,G12?,G13?,...,G1k1??}

S { G 2 } = { G 21 , G 22 , G 23 , . . . , G 2 k 2 } S\{G_2\}=\{G_{21},G_{22},G_{23},...,G_{2k_2}\} S{G2?}={G21?,G22?,G23?,...,G2k2??}

. . . ... ...

S { G t } = { G t 1 , G t 2 , G t 3 , . . . , G t k t } S\{G_t\}=\{G_{t1},G_{t2},G_{t3},...,G_{tk_t}\} S{Gt?}={Gt1?,Gt2?,Gt3?,...,Gtkt??}

因为这些图之间都是不同的,我们可以得到 ∑ i = 1 t ∣ S { G i } ∣ = 2 n ( n ? 1 ) 2 \sum_{i=1}^t|S\{G_i\}|=2^{\frac{n(n-1)}{2}} i=1t?S{Gi?}=22n(n?1)?

3. 观察被分到一起的标定图的组

然后我们取出其中一个集合进行观察,例如 S { G 1 } = { G 11 , G 12 , G 13 , . . . , G 1 k 1 } S\{G_1\}=\{G_{11},G_{12},G_{13},...,G_{1k_1}\} S{G1?}={G11?,G12?,G13?,...,G1k1??}

首先给定一个 G 1 G_1 G1?的非标定图,你给他任意标定,一定是与 G 1 G_1 G1?同构的。或者换一个说法,对于集合中的第一个元素 G 11 G_{11} G11?,你对它的顶点做任意的置换,得到的图 G 11 ′ G_{11}' G11? 一定是与 G 11 G_{11} G11? 同构的。

但是这里要注意,不是每次置换都会得到一个新的标定图。

例如下图中,左边为 G 11 G_{11} G11? 其六种置换中有两个与自身相同的,剩下的四种置换也只能得到两个不同的标定图。因此在这个例子中 ∣ S { G 1 } ∣ = 3 |S\{G_1\}|=3 S{G1?}=3

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eWPhoRRN-1639494629532)(C:\Users\user\AppData\Roaming\Typora\typora-user-images\image-20211214170228179.png)]

因此, S { G 1 } S\{G_1\} S{G1?} 中就是 G 11 G_{11} G11? 的置换去同构之后剩下的部分。

那么究竟有多少个置换呢?我们用集合 S i j S_{ij} Sij?表示从 G 1 i G_{1i} G1i?变成 G 1 j G_{1j} G1j?的所有置换。例如 S 12 S_{12} S12?表示从 G 11 G_{11} G11? 可以映射到 G 12 G_{12} G12? 的所有可能的映射

我们知道所有的置换数相加应该等于 n ! n! n!,那么我们以 G 11 G_{11} G11? 为置换的作用图,可以得到:

∑ i = 1 k 1 ∣ S 1 i ∣ = n ! \sum_{i=1}^{k_1}|S_{1i}|=n! i=1k1??S1i?=n!

通过上面的例子观察可以发现一个奇妙但是又比较自然的规律:

∣ S 11 ∣ = ∣ S 12 ∣ = ∣ S 13 ∣ = . . . |S_{11}|=|S_{12}|=|S_{13}|=... S11?=S12?=S13?=...

即从 G 11 G_{11} G11? 出发得到自身和得到其他图的映射的数目相等。

那么接下来我们对它进行一个证明:

首先设我们的 ∣ S 11 ∣ = x . S 11 = { f 1 , f 2 , . . . , f x } |S_{11}| = x. S_{11}=\{f_1,f_2,...,f_x\} S11?=x.S11?={f1?,f2?,...,fx?},即我们自己置换到自己的数量为 x x x,接着我们来尝试推 S 12 S_{12} S12? 的数目

首先,我们一定会存在一个映射 f f f,使得 f ? G 11 = G 12 f\cdot G_{11}=G_{12} f?G11?=G12?

那么我们可以得到 ? f i ∈ S 11 , ?? f ? f i ? G 11 = G 12 \forall f_i\in S_{11},\ \ f\cdot f_i\cdot G_{11} = G_{12} ?fi?S11?,??f?fi??G11?=G12?,同时 f i f_i fi?互不相同可以推出 f ? f i f\cdot f_i f?fi?也互不相同,所以每有一个可以到自己的置换 f i f_i fi?,就存在一个到 G 12 G_{12} G12?的置换 f ? f i f\cdot f_i f?fi?,并且置换之间互不相同

那么也就是说, ? f i ∈ S 11 , ?? f ? f i ∈ S 12 \forall f_i\in S_{11},\ \ f\cdot f_i\in S_{12} ?fi?S11?,??f?fi?S12?,因此 ∣ S 12 ∣ ≥ ∣ S 11 ∣ |S_{12}|\ge|S_{11}| S12?S11?

接着我们设 S 12 = { g 1 , g 2 , . . . , g y } S_{12}=\{g_1,g_2,...,g_y\} S12?={g1?,g2?,...,gy?}

那么 S 12 S_{12} S12? 中的映射满足 ? g i ∈ S 12 , ?? g i ? G 11 = G 12 \forall g_i\in S_{12},\ \ g_i\cdot G_{11} = G_{12} ?gi?S12?,??gi??G11?=G12?,同时对于刚刚的满足 f ? G 11 = G 12 f\cdot G_{11}=G_{12} f?G11?=G12? 的映射 f f f,我们可以找到它的逆映射 f ? 1 f^{-1} f?1,那么我们就有 f ? 1 ? G 12 = G 11 f^{-1}\cdot G_{12}=G_{11} f?1?G12?=G11?

那么我们可以得到 ? g i ∈ S 12 , ?? f ? 1 ? g i ? G 11 = G 11 \forall g_i\in S_{12},\ \ f^{-1}\cdot g_i\cdot G_{11} = G_{11} ?gi?S12?,??f?1?gi??G11?=G11?,同时 g i g_i gi?互不相同可以推出 f ? 1 ? g i f^{-1}\cdot g_i f?1?gi?也互不相同,所以每有一个可以到 G 12 G_{12} G12?的置换$ g_i , 就 存 在 一 个 到 自 己 的 置 换 ,就存在一个到自己的置换 f^{-1}\cdot g_i$,并且置换之间互不相同

那么也就是说, ? g i ∈ S 12 , ?? f ? 1 ? g i ∈ S 11 \forall g_i\in S_{12},\ \ f^{-1}\cdot g_i\in S_{11} ?gi?S12?,??f?1?gi?S11?,因此 ∣ S 12 ∣ ≤ ∣ S 11 ∣ |S_{12}|\le|S_{11}| S12?S11?

综上可得: ∣ S 11 ∣ = ∣ S 12 ∣ |S_{11}|=|S_{12}| S11?=S12?

同理可得:

∣ S 11 ∣ = ∣ S 12 ∣ = ∣ S 13 ∣ = . . . = ∣ S 1 k 1 ∣ |S_{11}|=|S_{12}|=|S_{13}|=...=|S_{1k_1}| S11?=S12?=S13?=...=S1k1??

∣ S 22 ∣ = ∣ S 21 ∣ = ∣ S 23 ∣ = . . . = ∣ S 2 k 1 ∣ |S_{22}|=|S_{21}|=|S_{23}|=...=|S_{2k_1}| S22?=S21?=S23?=...=S2k1??

. . . ... ...

∣ S k 1 k 1 ∣ = ∣ S k 1 1 ∣ = ∣ S k 1 2 ∣ = . . . = ∣ S k 1 k 1 ? 1 ∣ |S_{k_1k_1}|=|S_{k_11}|=|S_{k_12}|=...=|S_{k_1k_1-1}| Sk1?k1??=Sk1?1?=Sk1?2?=...=Sk1?k1??1?

同时,只需要一组逆映射,则可以推出 ∣ S i j ∣ = ∣ S j i ∣ |S_{ij}|=|S_{ji}| Sij?=Sji?,即从图 G 1 i G_{1i} G1i? 到图 G 1 j G_{1j} G1j? 的映射数量一定等于从图 G 1 j G_{1j} G1j? 到图 G 1 i G_{1i} G1i? 的映射数量

因此我们可以将那个从 G 11 G_{11} G11?出发的式子转化为

∑ i = 1 k 1 ∣ S i i ∣ = n ! \sum_{i=1}^{k_1}|S_{ii}|=n! i=1k1??Sii?=n!

4. 回到组与组之间

我们对第一组进行了一定的分析,得到了上面的式子 ∑ i = 1 k 1 ∣ S i i ∣ = n ! \sum_{i=1}^{k_1}|S_{ii}|=n! i=1k1??Sii?=n!

为了方便区分这是第几组,我们给 S S S 加上上标 ( 1 ) ^{(1)} (1) ∑ i = 1 k 1 ∣ S i i ( 1 ) ∣ = n ! \sum_{i=1}^{k_1}|S_{ii}^{(1)}|=n! i=1k1??Sii(1)?=n!

那么对于所有的 t t t 组同构所构成的集合,我们都可以得到一个这样的等式: ? 1 ≤ p ≤ t , ∑ i = 1 k p ∣ S i i ( p ) ∣ = n ! \forall 1\le p\le t,\sum_{i=1}^{k_p}|S_{ii}^{(p)}|=n! ?1pt,i=1kp??Sii(p)?=n!

那我们将这里所有的等式加起来就可以得到:
∑ p = 1 t ∑ i = 1 k p ∣ S i i ( p ) ∣ = t ? n ! t = ∑ p = 1 t ∑ i = 1 k p ∣ S i i ( p ) ∣ n ! \sum_{p=1}^{t}\sum_{i=1}^{k_p}|S^{(p)}_{ii}|=t*n!\\ t=\frac{\sum_{p=1}^{t}\sum_{i=1}^{k_p}|S^{(p)}_{ii}|}{n!} p=1t?i=1kp??Sii(p)?=t?n!t=n!p=1t?i=1kp??Sii(p)??
而我们回顾我们分组的过程,就可以发现 ∑ p = 1 t ∑ i = 1 k p \sum_{p=1}^{t}\sum_{i=1}^{k_p} p=1t?i=1kp?? 其实就是对所有的标定图 G G G ∣ S i i ( p ) ∣ |S^{(p)}_{ii}| Sii(p)? 就是对于这个图 G G G 它的能置换到自己的置换的数目。

那么这个求和,其实就是找到所有的特定的置换与图组成的二元组 ( f , G ) (f, G) (f,G),其中 f ? G = G f\cdot G=G f?G=G

那么在计算机中,枚举每个图的代价是巨大的,正如之前算的是 2 n ( n ? 1 ) 2 2^{\frac{n(n-1)}{2}} 22n(n?1)?,但是我们换一种思路,我们可以来枚举所有的置换 f f f,找到所有的满足 f ? G = G f\cdot G=G f?G=G 的图G,设 S G { f } S_G\{f\} SG?{f} 表示满足的图的集合,这样我们可以得到下面的式子
t = ∑ f ∣ S G { f } ∣ n ! t=\frac{\sum_f|S_G\{f\}|}{n!} t=n!f?SG?{f}?
而枚举置换的复杂度就只有 O ( n ! ) O(n!) O(n!) 了(其实和之前那个一样都是指数级的,只是那个显然更高阶

5. 计算给定了置换 f f f 之后有多少个图满足 f ? G = G f\cdot G=G f?G=G

首先我们知道图是通过边集的来定义的

而这里的点集的置换一定对应一个边集的置换

例如

{ 1 , 2 , 3 , 4 } → { 2 , 3 , 1 , 4 } \{1,2,3,4\} \rightarrow \{2,3,1,4\} {1,2,3,4}{2,3,1,4} 的点集置换 f f f 就可以得到一个相应的边集置换 f ′ f' f

{ ( 1 , 2 ) , ( 1 , 3 ) , ( 1 , 4 ) , ( 2 , 3 ) , ( 2 , 4 ) , ( 3 , 4 ) } → { ( 2 , 3 ) , ( 1 , 2 ) , ( 2 , 4 ) , ( 1 , 3 ) , ( 3 , 4 ) , ( 1 , 4 ) } \{(1,2), (1,3), (1,4), (2,3), (2,4), (3,4)\} \rightarrow \{(2,3), (1,2), (2,4), (1,3), (3,4), (1,4)\} {(1,2),(1,3),(1,4),(2,3),(2,4),(3,4)}{(2,3),(1,2),(2,4),(1,3),(3,4),(1,4)}

那么我们考虑这个点集所对应的满足自己映射到自己的所有的图,其实就是看这个边集置换下的能映射到自己的图:

那么我们就需要找到的图会在拥有了一条边之后,就一定拥有了其映射之后得到的另一条边,以此类推,直至形成环形。

那么我们先选择集合中的一条边 e 1 e_1 e1?对其进行边集置换得到 e 2 e_2 e2?,再依次进行,直到得到第一个等于之前出现过的边 e k + 1 e_{k+1} ek+1?为止,这样我们就得到了 k k k 条不同的边构成了一个置换的循环。然后继续选择集合中不同于这 k k k 条边的其它边几次进行这个操作。

首先我们可以证明 e k + 1 = e 1 e_{k+1}=e_1 ek+1?=e1?

因为假设 e k + 1 = e i , ( i > 1 ) e_{k+1}=e_i,(i>1) ek+1?=ei?,(i>1),那么根据 f ′ ? e i ? 1 = e i f'\cdot e_{i-1}=e_i f?ei?1?=ei?,就有 f ′ ? 1 ? e i = e i ? 1 f'^{-1}\cdot e_i=e_{i-1} f?1?ei?=ei?1?,那么根据 e i = e k + 1 e_i=e_{k+1} ei?=ek+1? 就有 f ′ ? 1 ? e k + 1 = e i ? 1 f'^{-1}*e_{k+1}=e_{i-1} f?1?ek+1?=ei?1? 也即 e k = e i ? 1 e_k=e_{i-1} ek?=ei?1? 那么违反了 e k + 1 e_{k+1} ek+1?是第一个与之前重复的假设。所以 e k + 1 e_{k+1} ek+1? 一定等于 e 1 e_1 e1?

例如上面这个例子:

我们先选择 ( 1 , 2 ) (1,2) (1,2) 这条边

( 1 , 2 ) → ( 2 , 3 ) → ( 1 , 3 ) → ( 1 , 2 ) {(1,2) \rightarrow (2,3) \rightarrow (1,3) \rightarrow (1,2)} (1,2)(2,3)(1,3)(1,2)

再选择 ( 1 , 4 ) (1,4) (1,4) 这条边

( 1 , 4 ) → ( 2 , 4 ) → ( 3 , 4 ) → ( 1 , 4 ) {(1,4) \rightarrow (2,4) \rightarrow (3,4) \rightarrow (1,4)} (1,4)(2,4)(3,4)(1,4)

因此我们就得到了两个长度为 3 3 3 的循环节

那么在这样一个映射下,这两个循环节里的边要么都选要么都不选,因此我们一共有 2 2 = 4 2^2=4 22=4 种方法

更加通式的情况,对于一个点集映射 f f f,我们可以得到它的边集映射 f ′ f' f,在 f ′ f' f 中我们可以通过遍历的方法得到它的循环节个数,设这个个数为 I ( f ′ ) I(f') I(f)

那么对于点集映射 f f f 的满足 f ? G = G f\cdot G=G f?G=G G G G 的个数就等于 2 I ( f ′ ) 2^{I(f')} 2I(f)

6. 最终公式

t = ∑ f 2 I ( f ′ ) n ! t=\frac{\sum_f2^{I(f')}}{n!} t=n!f?2I(f)?

7. 实现代码

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
    
const int maxn = 13;

int n;
long long sum = 0;
long long lv[maxn];
int f[maxn];
bool visEdge[maxn][maxn];
bool vis[maxn];

void calculate(){
    int cnt = 0;
    memset(visEdge, 0, sizeof(visEdge));
	for(int i = 1; i <= n; i++){
		for(int j = i+1; j <= n; j++)
        	if(!visEdge[i][j]){
                int ni = i, nj = j;
                do{
                	visEdge[ni][nj] = true;    
                	ni = f[ni], j = f[nj];
                    if(ni > nj) swap(ni, nj);
                }while(visEdge[ni][nj] == false);
        		cnt ++;
            }
    }
    long long s = 1;
    for(int i = 0; i < cnt; i++)
		s <<= 1;
    sum += s;
}

void search(int t){
	if(t == n + 1){
		calculate();
    	return ;
    }
    for(int i = 1; i <= n; i ++)
        if(!vis[i]){
			vis[i] = true;
            f[t] = i;
            search(t + 1);
            vis[i] = false;
        }
}

int main(){
	scanf("%d", &n);
    lv[0] = 1;
    for(int i = 1; i <= n; i++)
        lv[i] = lv[i - 1] * i;
    search(1);
    printf("%lld", sum/lv[n]);
    return 0;
}

/*
时间复杂度:O(n!*n^2) = O(n!)
运行结果:
1: 1
2: 2
3: 4
4: 11
5: 34
6: 156
7: 1044
8: 12346
9: 274668
10: 12005168
*/
  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2021-12-15 18:33:00  更:2021-12-15 18:35:01 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 16:51:25-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码