[ABC261D] Flipping and Bonus 题解

[ABC261D] Flipping and Bonus Solution

更好的阅读体验戳此进入

题面

掷 $ n $ 次硬币,同时存在计数器,初始为 $ 0 $。第 $ i $ 次投掷若为正面则计数器 $ +1 $ 并获得 $ X_i $ 元。若为背面则清空计数器并不获得钱。额外地,有 $ m $ 种连胜奖励,即示数为 $ C_i $ 时额外奖励 $ Y_i $ 元。求可能的最大收益。

Solution

题意较为明显,再看数据范围,显然 DP。不难想到设状态 $ dp(i, j) $ 表示投掷完第 $ i $ 次并且此时示数为 $ j $,我们特别地令 $ Y_i $ 表示示数为 $ i $ 时的奖励,无奖励时认为为 $ 0 $,显然可以通过 map 维护。显然边界均为 $ 0 $ 即可,则转移显然为:

\[dp(i, j) = \left\{ \begin{array}{ll} dp(i - 1, j - 1) + X_i + Y_j & \quad j \neq 0 \\ \max_{k = 0}^{i - 1}\{dp(i - 1, k)\} & \quad j = 0 \end{array} \right. \]

也就是如果继续连胜则由上次转移,否则可以由任意示数转移到示数为 $ 0 $,后者式子可以优化到 $ O(1) $ 但没必要,最终为 $ \texttt{2D0D} $ 的 DP,复杂度 $ O(n^2) $,可以通过。

最终答案即为 $ \max_{i = 0}^n dp(n, i) $。

Code

#define _USE_MATH_DEFINES
#include <bits/stdc++.h>

#define PI M_PI
#define E M_E
#define npt nullptr
#define SON i->to
#define OPNEW void* operator new(size_t)
#define ROPNEW(arr) void* Edge::operator new(size_t){static Edge* P = arr; return P++;}

using namespace std;

mt19937 rnd(random_device{}());
int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
bool rnddd(int x){return rndd(1, 100) <= x;}

typedef unsigned int uint;
typedef unsigned long long unll;
typedef long long ll;
typedef long double ld;

template < typename T = int >
inline T read(void);

int N, M;
ll X[5100];
unordered_map < ll, ll > mp;
ll dp[5100][5100];
ll ans(0);
ll Y(int idx){
    if(mp.find(idx) == mp.end())return 0;
    return mp[idx];
}

int main(){
    N = read(), M = read();
    for(int i = 1; i <= N; ++i)X[i] = read();
    for(int i = 1; i <= M; ++i){
        int C = read(), Y = read();
        mp.insert({C, Y});
    }
    for(int i = 1; i <= N; ++i){
        for(int k = 0; k <= i - 1; ++k)dp[i][0] = max(dp[i][0], dp[i - 1][k]);
        for(int j = 1; j <= i; ++j)dp[i][j] = dp[i - 1][j - 1] + X[i] + Y(j);
    }
    for(int i = 0; i <= N; ++i)ans = max(ans, dp[N][i]);
    printf("%lld\n", ans);
    fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
    return 0;
}

template < typename T >
inline T read(void){
    T ret(0);
    int flag(1);
    char c = getchar();
    while(c != '-' && !isdigit(c))c = getchar();
    if(c == '-')flag = -1, c = getchar();
    while(isdigit(c)){
        ret *= 10;
        ret += int(c - '0');
        c = getchar();
    }
    ret *= flag;
    return ret;
}

UPD

update-2022_12_24 初稿

posted @ 2023-01-07 15:46  Tsawke  阅读(16)  评论(0编辑  收藏  举报