• 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;
    }
    
  • 相关阅读:
    BZOJ 1630/2023 Ant Counting 数蚂蚁
    BZOJ 3997 组合数学
    BZOJ 2200 道路与航线
    BZOJ 3181 BROJ
    BZOJ 4011 落忆枫音
    BZOJ 4027 兔子与樱花
    vijos 1741 观光公交
    vijos 1776 关押罪犯
    vijos 1780 开车旅行
    5、异步通知机制
  • 原文地址:https://www.cnblogs.com/ZX-GO/p/14991079.html
Copyright © 2020-2023  润新知