[Codeforces 11D] A Simple Task

原题传送门

题意简述:

给定n个点和m条边,接下来m行每行x,y表示两个连通的点,输出图中的环数

题目分析:

一看数据n<=19,马上想到状压DP记录状态

设 f[i][j]表示状态为i,结束点为j,起点为状态i中lowbit最小的点的路径中的环数,枚举下一个节点,当两个节点相同时证明出现了一个环,由于无向图的情况,会出现::

  1. 同一个环由于可以从两个方向遍历所以会被记录两次
  2. 一条边和两个端点出现的非法环(两个点因可以相互到达而被误判成环的情况)

故最后的答案ans = (ans - m) / 2

code:

/* Codeforces11D -- A Simple Task */
#include <bits/stdc++.h>
#define int long long

int n, m, ans;
int f[1 << 19][19];
bool mp[25][25];

inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while (!isdigit(ch))
        f = (ch == '-') ? -1 : 1, ch = getchar();
    while (isdigit(ch))
        x = x * 10 + (ch - '0'), ch = getchar();
    return x * f;
}

main()
{
    // freopen("in.txt", "r", stdin);
    n = read(), m = read();
    int x, y;
    int MaxState = 1 << n;
    for (int i = 1; i <= m; i++)
        x = read() - 1, y = read() - 1, mp[x][y] = mp[y][x] = true;
    for (int i = 0; i < n; i++)
        f[1 << i][i] = 1; /* 初始化 */
    for (int i = 1; i <= MaxState; i++)
        for (int j = 0; j < n; j++)
        {
            if (!f[i][j]) /* 无环图 */
                continue;
            for (int k = 0; k < n; k++) /* 枚举下一节点 */
            {
                if (!mp[j][k]) /* 不连通就直接跳过 */
                    continue;
                if ((i & -i) > (1 << k)) /* lowbit > (1 << k)表示k点编号小于i点编号,但是起点不可以更改 */
                    continue;
                if ((1 << k) & i) /* 构成一个环 */
                {
                    if ((1 << k) == (i & -i)) /* 这个环回到了起点 */
                        ans += f[i][j];
                }
                else
                    f[i | (1 << k)][k] += f[i][j]; /* 状态转移 */
            }
        }
    std::cout << (ans - m) / 2 << '\n';
}

猜你喜欢

转载自www.cnblogs.com/wyctstf/p/11372158.html