zoukankan      html  css  js  c++  java
  • 12770 小L的有向图

    题意

     首先,我们定义一个图的价值为其合法拓扑序的个数。

    对于一张n个点,m条边的有向图, 它的每一条都可能消失,求其所有形态的价值的和。

    (n leq 22, m leq n imes (n-1))
    数据保证没有重边,自环(x到y的边和y到x的边不算重边)。

    第一个问题:给定一个有向图,求拓扑排序生成的序列数。

    首先,我们要明白什么是拓扑序(如果知道什么是拓扑序请略过)

    个人理解,拓扑序是拓扑排序后的序列

    以下图为例:

    a点没有被其它点指向,d点指向其他任何点,所以拓扑序应该是 a xxx d的格式。

    其次,c点要在b点之后,所以上图的拓扑序只有:aebcd、abecd、abced 三个。

    接下来是对拓扑序的求解。

    可定义状态s的二进制位上的1表示此点已经排好序了。

    例如:mask=6时,化为二进制mask=110,表示第2、3个点已经排好序了。

    当所有儿子节点排好序的时候,父节点就排好序了。

    所以父节点的状态可以由子节点转移而来。

    用son[i]表示节点i可以进行转移的合法状态,dp[mask]表示状态为mask的方法数。

    for (int mask = 0; mask < all; ++mask) {
      for (int i = 1; i <= n; ++i) {
        if((mask & (1<<i-1) == 0) && (mask & son[i]) == son[i]) {
          dp[mask | (1<<i-1)] += dp[mask];
        }
      }
    }
    

    思路

    在已知如何求解正常拓扑序个数的基础上,考虑在一个每条边都可能消失的有向图上的所有合法拓扑序的数量。

    每个父节点的状态依旧是由子节点转移而来,而由于每条边都可能消失,所以每个子节点对父节点的贡献都是 (dp[mask] * 2) 的。所以,父节点的状态转移方程为:

    [dp[mask | (1<<i-1)] = sum {dp[mask] * 2 ^{mask中i的子节点个数}} ]

    Code

    #include <bits/stdc++.h>
    
    using namespace std;
    const int mod = 998244353;
    const int maxn = (1<<22)+10;
    typedef long long ll;
    
    int n, m, all;
    ll dp[maxn] = {1};
    int son[24], two[24] = {1}, cnt[maxn];
    
    void add(ll &x, ll y) {
        x += y;
        if(x >= mod) x -= mod;
    }
    
    int main() {
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= n; ++i) two[i] = (two[i-1] << 1) % mod;
        for (int u, v, i = 1; i <= m; ++i) {
            scanf("%d %d", &u, &v);
            son[u] |= (1<<v-1);
        }
        all = 1 << n;
        for (int i = 1; i < all; ++i) cnt[i] = cnt[i>>1] + (i&1);
        for (int mask = 0; mask < all; ++mask) {
            for (int i = 1; i <= n; ++i) {
                if(!(mask & (1<<i-1)))
                    add(dp[mask | (1<<i-1)], dp[mask] * two[cnt[son[i] & mask]] % mod);
            }
        }
        printf("%lld
    ", dp[all-1]);
        return 0;
    }
    
  • 相关阅读:
    设计模式与23种设计模式的简单介绍
    一文读懂C++ Vector在算法竞赛中的常见用法
    一文读懂C++ String类在算法竞赛中的常见用法
    GO语言的单元测试与性能测试
    变量提升和函数提升及二者优先级
    闭包
    读《你不知道的JavaScript 中》-异步【3】Promise
    js数组方法-改变原数组和不改变原数组
    读《你不知道的JavaScript 中》-异步【2】回调
    组合类算法问题
  • 原文地址:https://www.cnblogs.com/acerkoo/p/11141392.html
Copyright © 2011-2022 走看看