Codeforces11 D. A Simple Task(统计环个数,状压DP)

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点,也就是枚举环中相邻两点。
这样可以定义状态 d p [ s ] [ i ] dp[s][i] 代表状态可选状态点集为 s s ,且 s s 中最小点和 i i 点连通的方案数。
由此初始化 d p [ 1 < < i ] [ i ] = 1 dp[1<<i][i]=1

那么转移的话,就是再枚举 i i 所连接的下一个点 j j ,如果 j j 不在点集 s s 中,那么有 d p [ s ( 1 < < j ) ] [ j ] + = d p [ s ] [ i ] dp[s|(1<<j)][j]+=dp[s][i]

如果 j j 在点集中,那无需转移,但是如果 j j 恰好是 s s 点集中最小点,那说明成环了,加上 d p [ s ] [ i ] dp[s][i] 作为结果。

注意两点的情况会被算上,所以要减掉边数 m m
同时每个环会被算两次,最后还要除以二。

#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;
}

猜你喜欢

转载自blog.csdn.net/tomjobs/article/details/108350582