AtCoder Beginner Contest 275
E - Sugoroku 4
题解:简单的概率dp
- 记忆化搜索,两层dp, 一层为当前的位置,一层为剩余的跳数
- 答案即为 sum( dfs(0,i) )
- 终止状态为 num=0 或 u=n, 当同时满足,此时返回该方案,否则返回0
- 时间复杂度为 O(1000* 1000* 10), 也可以直接三层dp ,但记忆化写的方便些
部分代码:
ll dfs(int u, int num) {
if (num == 0 || u == n) {
if (u == n && num == 0) return 1;
else return 0;
}
if (dp[u][num] != -1) return dp[u][num];
ll res = 0;
for (int i = 1;i <= m;i++) {
int v = u + i;
if (v > n) {
int add = v - n;
res = (res + dfs(v - 2 * add, num - 1) * inv % mod) % mod;
}
else res = (res + dfs(v, num - 1) * inv % mod) % mod;
}
return dp[u][num] = res;
}
void solve() {
cin >> n >> m >> k;
ll res = 0;
inv = qpow(m, mod - 2); // 1/m的值
memset(dp, -1, sizeof(dp));
for (int i = 1;i <= k;i++) res = (res + dfs(0, i)) % mod;
cout << res << '\n';
}
F - Erase Subarrays
题解:比赛题解已经很详细了,就是转化成 b[i]*a[i] 的形式,b[i]=1则选,b[i]=0则不选。分析一下 b数组与解的关系,易得若1后面出现0或b[1]=0, 权值都会加1,即删过。则可设初始状态为1,避免分类讨论。后面就是一个dp。因为结果只与当前的数和上一位为0或为1有关,则时间复杂度为 O(n * m * 2),完全够。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn = 3e3 + 10;
const ll mod = 998244353;
int inf = 1e9 + 7;
int n, m, a[maxn], b[maxn];
int dp[maxn][maxn][2]; // 遍历到 i , 使得到 t 的值最小,b[i-1]为 1 或 0
void solve() {
cin >> n >> m;
for (int i = 1;i <= n;i++) cin >> a[i];
memset(dp, inf, sizeof(dp));
inf = dp[0][0][0];
dp[0][0][1] = 0; // 结尾为1, 则若接一个 0 增加权值为1
for (int i = 1;i <= n;i++) {
for (int t = 0;t <= m;t++) { // 上一次到的值
if (t + a[i] <= m) {
dp[i][t + a[i]][1] = min(dp[i][t + a[i]][1], dp[i-1][t][0]);
dp[i][t + a[i]][1] = min(dp[i][t + a[i]][1], dp[i-1][t][1]);
}
dp[i][t][0] = min(dp[i][t][0], dp[i - 1][t][0]);
dp[i][t][0] = min(dp[i][t][0], dp[i - 1][t][1] + 1);
}
}
for (int i = 1;i <= m;i++) {
int res = min(dp[n][i][0], dp[n][i][1]);
if (res == inf) cout << -1 << '\n';
else cout << res << '\n';
}
}
int main() {
int t = 1;
//cin >> t;
while (t--) {
solve();
}
return 0;
}