• AtCoder Beginner Contest 212 E(DP)


    AtCoder Beginner Contest 212

    ABC 212

    E - Safety Journey

    题意

    一个完全图(即任意两点之间都有一条双向道路),给定 m m m条废弃的道路,问从 1 1 1号城市出发走 k k k步走回 1 1 1号的方案有多少种。

    思路

    观察数据范围 n < = 5000 n <= 5000 n<=5000 m < = 5000 m <= 5000 m<=5000 k < = 5000 k<=5000 k<=5000可以考虑二维暴力DP 。
    定义DP数组 f [ i ] [ j ] f[i][j] f[i][j] : i i i步走到城市 j j j的方案数
    对于每一步枚举从城市 k k k出发,可以得到dp方程 f [ i ] [ j ] = ∑ k = 1 n f [ i − 1 ] [ k ] f[i][j] = \sum_{k=1}^{n}f[i - 1][k] f[i][j]=k=1nf[i1][k](城市 k k k到城市 j j j的道路存在且还没有废弃)
    估计时间复杂度 因为枚举步数,出发城市,存在的道路都是O(n)的 循环嵌套时间复杂度为 O ( n 3 ) O(n^3) O(n3) 显然不足以通过此题。

    考虑如何优化,因为dp方程的定义已经确定,所以只能从枚举每个城市连接的道路着手。
    时间复杂度在于,因为是完全图删去的道路最多为 5000 5000 5000条,所以对于每个城市直接连接的城市仍然有很多。
    正难则反,因为删去的道路很少,我们可以枚举出发城市 k k k所不能到达的城市,而不是能到达的城市。

    在前一次dp求解中记录下 s u m = ∑ j = 1 n f [ i − 1 ] [ j ] sum = \sum_{j=1}^{n}f[i - 1][j] sum=j=1nf[i1][j] 在枚举城市 k k k s u b = ∑ k = 1 n f [ i − 1 ] [ j ] sub = \sum_{k=1}^{n}f[i - 1][j] sub=k=1nf[i1][j]( k k k不能到达 j j j)
    那么答案就是 f [ i ] [ j ] = ∑ k = 1 n f [ i − 1 ] [ k ] f[i][j] = \sum_{k=1}^{n}f[i - 1][k] f[i][j]=k=1nf[i1][k](城市 k k k到城市 j j j的道路存在且还没有废弃) = s u m − s u b = sum - sub =sumsub
    因为删除的道路很少,在每次循环中遍历的次数之和恰好为 m ∗ 2 m * 2 m2
    时间复杂度优化到 O ( n ∗ m ) O(n * m) O(nm)

    代码

    #include 
    #include 
    #include 
    using namespace std;
    #define ll long long
    const int N = 5010, mod = 998244353;
    int n,m,k;
    int f[N][N];//第i步走到j地的方案
    vector<int>g[N];
    int main()
    {
        scanf("%d%d%d",&n,&m,&k);
        for(int i = 1; i <= m; i ++){
            int u,v;
            scanf("%d%d",&u,&v);
            g[u].push_back(v);
            g[v].push_back(u);
        }
        f[0][1] = 1;
        ll sum = 1;//f[i - 1]的道路方案数总和
        for(int i = 1; i <= k; i ++)
        {
            ll tmp = 0;
            for(int j = 1; j <= n; j ++)
            {
                ll sub = f[i - 1][j];//道路废弃的城市方案数减去,初始化为f[i-1][j]因为自己不能到自己
                for(int v : g[j]){
                    sub = (sub + f[i - 1][v]) % mod;
                }
                f[i][j] = (sum + mod - sub) % mod;
                tmp = (tmp + f[i][j]) % mod;//记录sum
            }
            sum = tmp;
        }
        printf("%d\n",f[k][1]);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    更新:
    E:2022.11.4
    F待补

  • 相关阅读:
    为什么游戏里的都是伪随机,做不出真随机?
    学习黑马AJAX
    day35 代码回想录 柠檬水找零&根据身高重建队列&用最少数量的箭引爆气球
    CSS-浮动,定位
    基于深度学习的车牌+车辆识别(YOLOv5和CNN)
    力扣1148. 文章浏览 I
    docker总结
    【2022秋线上作业-第5次-第11-13周】选择题
    rails console打印实例变量
    Tomcat 源码分析 (Digester类的使用) (十)
  • 原文地址:https://blog.csdn.net/TT6899911/article/details/127698325