• Codeforces Round #688 (Div. 2) CF1453F. Even Harder dp及优化


    CF Round 688(Div. 2) F. Even Harder

    gzh
    ewm

    题目链接:https://codeforces.com/contest/1453/problem/F

    (n)个位置,每个位置有一个权值(a_i)(0le a_ile n - i)

    当你在第(i)个位置时,你可以移动到([i+1,i+a_i])位置中的某一个(也就是说(a_igt 0)才能移动)。

    最后移动到(n)则游戏胜利。

    此时可能有多种游戏胜利的走法,我希望这个游戏只能由一种胜利的走法。

    现在你可以将某些位置的权值改变为(0),问最少改变几个位置,使得游戏只有一种胜利的走法。

    注意:合法的路径中经过的位置除了(a_n)外不能有(0)

    (2le nle 3000)

    样例:
    3
    4
    1 1 1 0
    5
    4 3 2 1 0
    9
    4 1 4 2 1 0 2 1 0
    ---
    0
    3
    2
    

    思路

    思路参考:https://www.cnblogs.com/Heltion/p/14088245.html

    希望能尽量讲清楚,大佬的思路确实很厉害。


    如果唯一路径是(u1=1,u2,⋯,um=n),那么要满足(u_{i+1}≤u_i+a_{ui}<u_{i+2}),并且对于所有(u_i<j<u_{i+1},j+a_j<u_{i+1}).

    (dp[i][j][k])表示从(n)开始考虑到第(i)位,唯一路径后两个位置位(j)(k)的最小代价.

    两种转移:

    • (i)不在当前路径中,(dp[i][j][k]←dp[i+1][j][k]+[j≤i+a_i]),因为(j≤i+a_i)的时候,需要把第(i)位修改成0,代价(+1).
    • (i)在当前路径中,(dp[i][i][j]←dp[i+1][j][k]),需要满足(j≤i+a_i<k).

    这时候可以打出一个(O(n^3))暴力来:

    const int MXN = 3e3 + 5;
    int n, m;
    int ar[MXN];
    void work() {
        n = read();
        rep(i, 1, n + 1) ar[i] = read();
        vector<vector<vector<int>>> dp(n + 2, vector<vector<int>>(n + 2, vector<int>(n + 2, INF)));
        dp[n][n][n + 1] = 0;
        per(i, n - 1, 1) {
            //i不在当前路径中
            rep(j, i + 1, n + 1) {
                rep(k, j + 1, n + 2) {
                    dp[i][j][k] = min(dp[i][j][k], dp[i + 1][j][k] + (i + ar[i] >= j));
                }
            }
            if(ar[i] == 0) continue;
            //i在当前路径中
            rep(j, i + 1, i + ar[i] + 1) {
                rep(k, i + ar[i] + 1, n + 2) {
                    dp[i][i][j] = min(dp[i][i][j], dp[i + 1][j][k]);
                }
            }
        }
        int ans = INF;
        rep(i, 1, n) {
            rep(j, i + 1, n + 1) {
                if(1 + ar[1] >= i) ans = min(ans, dp[1][i][j]);
            }
        }
        printf("%d
    ", ans);
    }
    

    考虑如何优化这两个转移:

    优化一:

    第二种转移,对所有(i+1≤j≤i+a_i)(i)在当前路径中,(dp[i][i][j]←dp[i+1][j][k]),需要满足(j≤i+a_i<k)

    他的操作本质是(dp[][i][j]=min_{k>i+ai}dp[][j][k]).

    这个东西(min_{k>i+ai}dp[][j][k])维护一个后缀最小值即可解决:(suf[][j][k]=min(dp[][j][k],suf[][j][k+1]))

    修改一下有代码如下:

    const int MXN = 3e3 + 5;
    int n, m;
    int ar[MXN];
    void work() {
        n = read();
        rep(i, 1, n + 1) ar[i] = read();
        vector<vector<vector<int>>> dp(n + 2, vector<vector<int>>(n + 2, vector<int>(n + 2, INF))), suf(n + 2, vector<vector<int>>(n + 2, vector<int>(n + 2, INF)));
        dp[n][n][n + 1] = 0;
        suf[n][n][n + 1] = 0;
        per(i, n - 1, 1) {
            //i不在当前路径中
            rep(j, i + 1, n + 1) {
                rep(k, j + 1, n + 2) {
                    dp[i][j][k] = min(dp[i][j][k], dp[i + 1][j][k] + (i + ar[i] >= j));
                }
                suf[i][j][n + 1] = dp[i][j][n + 1];
                per(k, n, j + 1) {
                    suf[i][j][k] = min(suf[i][j][k + 1], dp[i][j][k]);
                }
            }
            if(ar[i] == 0) continue;
            //i在当前路径中
            rep(j, i + 1, i + ar[i] + 1) {
                dp[i][i][j] = min(dp[i][i][j], suf[i + 1][j][i + ar[i] + 1]);
            }
            suf[i][i][n + 1] = dp[i][i][n + 1];
            per(j, n, i + 1) {
                suf[i][i][j] = min(suf[i][i][j + 1], dp[i][i][j]);
            }
        }
        int ans = INF;
        rep(i, 1, n) {
            rep(j, i + 1, n + 1) {
                if(1 + ar[1] >= i) ans = min(ans, dp[1][i][j]);
            }
        }
        printf("%d
    ", ans);
    }
    

    优化二:

    第一种转移,对所有(i+1≤j≤i+a_i)(i)不在当前路径中,(dp[i][j][k]←dp[i+1][j][k]+[j≤i+a_i])(j)肯定要大于(i),就是要(a_igt 0)

    他的操作本质其实是(for;each;jin[i+1,i+a_i+1]:dp[][j][k]+=1),且(k)任意。

    那其实我最后只要用一个二维矩阵维护(dp[][j][k])的后两维,每次更新的时候主要更新(i)在唯一路径中的情况。

    (i)不在唯一路径中的情况的话,其实只要用(add[j]) 记录(j)全部加(1)操作的次数即可。

    最终代码如下:

    时间复杂度:(O(n^2)).

    #include <bits/stdc++.h>
    #define fi first
    #define se second
    #define o2(x) (x) * (x)
    #define mk make_pair
    #define eb emplace_back
    #define SZ(x) ((int)(x).size())
    #define all(x) (x).begin(), (x).end()
    #define clr(a, b) memset((a), (b), sizeof((a)))
    #define rep(i, s, t) for(register int i = (s), LIM=(t); i < LIM; ++i)
    #define per(i, s, t) for(register int i = (s), LIM=(t); i >= LIM; --i)
    #define GKD std::ios::sync_with_stdio(false);cin.tie(0)
    #define my_unique(x) sort(all(x)), x.erase(unique(all(x)), x.end())
    using namespace std;
    typedef long long LL;
    typedef long long int64;
    typedef unsigned long long uint64;
    typedef pair<int, int> pii;
    // mt19937 rng(time(NULL));//std::clock()
    // mt19937_64 rng64(chrono::steady_clock::now().time_since_epoch().count());
    // shuffle(arr, arr + n, rng64);
    inline int64 read() {
        int64 x = 0;int f = 0;char ch = getchar();
        while (ch < '0' || ch > '9') f |= (ch == '-'), ch = getchar();
        while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch =
        getchar(); return x = f ? -x : x;
    }
    inline void write(int64 x, bool f = true) {
        if (x == 0) {putchar('0'); if(f)putchar('
    ');else putchar(' ');return;}
        if (x < 0) {putchar('-');x = -x;}
        static char s[23];
        int l = 0;
        while (x != 0)s[l++] = x % 10 + 48, x /= 10;
        while (l)putchar(s[--l]);
        if(f)putchar('
    ');else putchar(' ');
    }
    int lowbit(int x) { return x & (-x); }
    template <class T>
    T big(const T &a1, const T &a2) {return a1 > a2 ? a1 : a2;}
    template <class T>
    T sml(const T &a1, const T &a2) {return a1 < a2 ? a1 : a2;}
    template <typename T, typename... R>
    T big(const T &f, const R &... r) {return big(f, big(r...));}
    template <typename T, typename... R>
    T sml(const T &f, const R &... r) {return sml(f, sml(r...));}
    void debug_out() { cout << '
    '; }
    template <typename T, typename... R>
    void debug_out(const T &f, const R &... r) {
        cout << f << " ";
        debug_out(r...);
    }
    #ifdef LH_LOCAL
    #define debug(...) cout << "[" << #__VA_ARGS__ << "]: ", debug_out(__VA_ARGS__);
    #else
    #define debug(...) ;
    #endif
    /*================Header Template==============*/
    const int mod = 998244353;// 998244353
    int ksm(int a, int64 b, int kmod = mod) {int res = 1;for(;b > 0;b >>= 1, a = (int64)a * a % kmod) if(b &1) res = (int64)res * a % kmod;return res;}
    const int INF = 0x3f3f3f3f;
    const int MXN = 3e3 + 5;
    int n, m;
    int ar[MXN];
    void work() {
        n = read();
        rep(i, 1, n + 1) ar[i] = read();
        vector<vector<int>> dp(n + 2, vector<int>(n + 2, INF)), suf(n + 2, vector<int>(n + 2, INF));
        vector<int> add(n + 2, 0);
        dp[n][n + 1] = 0;
        suf[n][n + 1] = 0;
        per(i, n - 1, 1) {
            rep(j, i + 1, n + 1) {
                if(j <= i + ar[i]) dp[i][j] = min(dp[i][j], suf[j][i + ar[i] + 1] + add[j]);
            }
            per(j, n, i) suf[i][j] = min(suf[i][j + 1], dp[i][j]);
            rep(j, i + 1, i + ar[i] + 1) ++ add[j];
        }
        printf("%d
    ", suf[1][2]);
    }
    
    int main() {
    #ifdef LH_LOCAL
        freopen("D:/ACM/mtxt/in.txt", "r", stdin);
        // freopen("D:/ACM/mtxt/out.txt", "w", stdout);
    #endif
        for(int cas = 1, tim = read(); cas <= tim; ++ cas) {
            // printf("Case #%d:
    ", cas);
            work();
        }
    #ifdef LH_LOCAL
        cout << "time cost:" << 1.0 * clock() / CLOCKS_PER_SEC << "s" << endl;
    #endif
        return 0;
    }
    
  • 相关阅读:
    Video视频播放中断问题排查记录
    下一站:手机安全
    数据之美 之一
    数据之美 之二
    数据之美 之三
    Groovy入门
    Java8新特性(Lambda表达式、Stream流、Optional类)等
    websocket和ajax的区别(和http的区别)
    java泛型<? extends E>和<? super E>的区别和适用场景
    JAVA反射
  • 原文地址:https://www.cnblogs.com/Cwolf9/p/14121339.html
Copyright © 2020-2023  润新知