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\n", (res - m) / 2);
}

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

    solve();
    return 0;
}
posted @ 2021-07-09 16:15  这知识他不进我的脑子  阅读(35)  评论(0编辑  收藏  举报