[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 初稿