CF-11D.A Simple Task【思维+状压dp】

思路
(dp[i][j]:)表示状态为(i)(1)表示已走到这个点,(0)表示未走到这个点,此时站在(j)点上的所有方案数。
发现我们在计算的时候可能会出现重复的情况,例如在计算(1,2,3)这个环时先按照顺序计算了(1,2,3),又计算了(2,3,1),这样可以发现是重复的情况。所以需要dp的限制就变成了:状态为(i),此时站在点(j)上,且这种状态的起点为状态(i)中的第一个为1的值。
接下来考虑转移,先考虑不合法的状态, 假设但前点在(j),接下来要走的点为(k),状态(i)的起点为(s),如果发现(k<s),那么就说明不能走到起点之前。
考虑合法状态:
假设已经在(j)点上,考虑(j)能到的点。如果(k)点没有到过,那么转移方程即为(dp[i|(1<<k)]+=dp[i][j])
考虑到过已经到过的点:此时(j)走过去的(k)点恰好是(s)点,那么就说明走成了一个环,就可以对答案产生贡献值(res+=dp[i][j])
如果走过去的(k)点不是(s)点,那么其产生的贡献会在(s=k)的那些状态中计算,不用考虑。
最后需要减去二元环的所有数即总数减边数(res-=m)
因为我们计算的时候没有考虑顺时针或者逆时针顺序(例如(1-2-3,1-3-2)),所以对最后的答案还要除以2:(res/=2)

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

typedef long long LL;
typedef pair<int, LL> PIL;
typedef pair<int, int> PII;
typedef pair<int, double> PID;
typedef unsigned long long ULL;
#define x first
#define y second
const int N = 500 + 10, M = 1e5 + 10;
const double PI = acos(-1.0);
const double eps = 1e-5;
const int mod = 1000000007;
const int inf = 0x3f3f3f3f;
const LL INF = 0x3f3f3f3f3f3f3f3f;
#define gcd __gcd

set<int> e[N];
LL dp[(1 << 19) + 10][20];

void solve() {
    int n, m;
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= m; i++) {
        int u, v;
        scanf("%d%d", &u, &v);
        u--; v--;
        e[u].insert(v);
        e[v].insert(u);
    }

    LL res = 0;
    for(int i = 0; i < n; i++) {
        dp[1 << i][i] = 1;
    }
    for(int i = 1; i < (1 << n); i++) {
        vector<int> vec;
        int mx = 20;
        for(int j = 0; j < n; j++) {
            if((i >> j) & 1) {
                vec.push_back(j);
                mx = min(mx, j);
            }
        }
        for(auto u : vec) {
            for(auto v : e[u]) {
                if(v < mx) continue;
                if(!((1 << v) & i)) {
                    dp[i | (1 << v)][v] = dp[i | (1 << v)][v] + dp[i][u];
                }
                else {
                    if(v == mx) {
                        res += dp[i][u];
                    }
                }
            }
        }
    }
    printf("%lld
", (res - m) / 2);
}

int main() {
    #ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    #endif // ONLINE_JUDGE

    solve();
    return 0;
}
原文地址:https://www.cnblogs.com/ZX-GO/p/14991079.html