Given a simple graph, output the number of simple cycles in it. A simple cycle is a cycle with no repeated vertices or edges.
Input
The first line of input contains two integers n and m (1 ≤ n ≤ 19, 0 ≤ m) – respectively the number of vertices and edges of the graph. Each of the subsequent m lines contains two integers a and b, (1 ≤ a, b ≤ n, a ≠ b) indicating that vertices a and b are connected by an undirected edge. There is no more than one edge connecting any pair of vertices.
Output
Output the number of cycles in the given graph.
Examples
inputCopy
4 6
1 2
1 3
1 4
2 3
2 4
3 4
outputCopy
7
Note
The example graph is a clique and contains four cycles of length 3 and three cycles of length 4.
题意:
无向图多少个简单环。
思路:
参考博客
看到这个数据范围可以想到是搜索或者状压。
状压实现的话,肯定一维状态是所选点集,考虑怎样用这个所选点集的信息凑出环。
只要能得到 sta->x1->x2->x3->…->end->sta的形式,就能得到环。也就是只需要知道sta和end两个点,而其他点我们不用管,我们只需要知道这是连通图就好了。
不妨把sta当做状态点集中最小的那个点,然后枚举end点,也就是枚举环中相邻两点。
这样可以定义状态
代表状态可选状态点集为
,且
中最小点和
点连通的方案数。
由此初始化
那么转移的话,就是再枚举 所连接的下一个点 ,如果 不在点集 中,那么有 。
如果 在点集中,那无需转移,但是如果 恰好是 点集中最小点,那说明成环了,加上 作为结果。
注意两点的情况会被算上,所以要减掉边数
。
同时每个环会被算两次,最后还要除以二。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long ll;
ll dp[1 << 19][20];
int maze[22][22];
int main() {
int n,m;scanf("%d%d",&n,&m);
for(int i = 1;i <= m;i++) {
int x,y;scanf("%d%d",&x,&y);
x--;y--;
maze[x][y] = maze[y][x] = 1;
}
for(int i = 0;i < n;i++) {
dp[1 << i][i] = 1;
}
ll ans = 0;
for(int i = 1;i < (1 << n);i++) {
int mi = log2(i & -i); //最小值点
for(int j = mi;j < n;j++) {
if(!dp[i][j]) continue; //要是为0的话就没贡献,没必要算了
for(int k = mi;k < n;k++) {
if(!maze[j][k]) {
continue;
}
if(i & (1 << k)) {
if(k == mi) ans += dp[i][j];
} else {
dp[i | (1 << k)][k] += dp[i][j];
}
}
}
}
printf("%lld\n",(ans - m) / 2);
return 0;
}