codeforces 11d 状压dp

题意:n个点m条边的一个图,求其中简单环共有多少个

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;
const int MAXN = 19;
const int INF = 0x3f3f3f3f;

int read(){
    int f = 1, x = 0;
	char s = getchar();
    while(s < '0' ||s > '9'){if(s == '-')f = -1; s = getchar();}  
    while(s >= '0' && s <= '9'){x = x * 10 + s - '0'; s = getchar();}
    return x * f;
}
int n, m;
int lowbit(int x){for(int i = 0; i < n; i ++)if(x & (1 << i))return i;}	//lowbit(S):找S二进制中最低非零位的位置
bool Map[MAXN + 5][MAXN + 5];
LL f[(1 << MAXN) + 5][MAXN + 5];//f[S][i]:点集S,此时访问到i点,起点为lowbit(S)
int main(){
    LL ans = 0;
    n = read(), m = read();
    for(int i = 1; i <= m; i ++){
        int x = read() - 1, y = read() - 1;
        Map[x][y] = Map[y][x] = 1;
    }
    for(int i = 0; i < n; i ++)
        f[1 << i][i] = 1;
        
    for(int S = 1; S <= (1 << n) - 1; S ++){//枚举点集
        for(int i = 0; i < n; i ++){//刷表只能由自己转移到别人
            if(! f[S][i]) continue;//自己状态必须合法访问过
            int p = lowbit(S);//p为当前集合S位置最小的一点
            for(int j = p; j < n; j ++){//访问比起点大的点作为下一点
                if(! Map[i][j]) continue;//必须与i连边
                if(S & (1 << j)){//j访问到当前点集
                    if(j == p)//j正好为起点出现了圈
                        ans += f[S][i];//累加答案
                }
                else//状态转移
                    f[S | (1 << j)][j] += f[S][i];
            }
        }
    }
    printf("%lld\n", (ans - m) / 2ll);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_38759433/article/details/86549553