AtCoder Educational DP Contest 刷题记录Ⅱ
H - Grid 1
原题链接:H - Grid 1
分析
二维动态规划,突然想起来小学奥数好像学过,不过奥数显然输入量没有这么大。
正解
#include <bits/stdc++.h>
#define mod 1000000007
using namespace std;
const int N = 1005;
int H, W;
char a[N][N];
int dp[N][N];
int main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cin >> H >> W;for (int i = 1; i <= H; i++){for (int j = 1; j <= W; j++){cin >> a[i][j];}}dp[1][1] = 1;for (int i = 2; i <= H; i++){dp[i][1] = (a[i][1] == '.') * dp[i - 1][1] % mod; }for (int j = 2; j <= W; j++){dp[1][j] = (a[1][j] == '.') * dp[1][j - 1] % mod; }for (int i = 2; i <= H; i++){for (int j = 2; j <= W; j++){if (a[i][j] == '#')dp[i][j] = 0;elsedp[i][j] = (dp[i - 1][j] + dp[i][j - 1]) % mod;}}cout << dp[H][W] % mod;
}
I - Coins
原题链接:I - Coins
分析
咋是绿了?写到绿的dp了/ll
好吧,蒟蒻并非会。
我们发现如果设表示掷前i个硬币正面朝上的硬币数多于反面朝上的硬币数的概率,几乎无法快速更新,所以多记录一维。
设表示掷前i个硬币正面朝上的硬币数为j的概率。
转移
依旧注意边界!
正解
#include <bits/stdc++.h>
using namespace std;
const int N = 3005;
int n;
double p[N], q[N];
double dp[N][N];
int main(){cin >> n;for (int i = 1; i <= n; i++){cin >> p[i];q[i] = 1.0 - p[i];}dp[0][0] = 1.0;dp[1][1] = p[1];dp[1][0] = q[1];for (int i = 2; i <= n; i++){for (int j = 0; j <= i; j++){dp[i][j] = dp[i - 1][j - 1] * p[i] + dp[i - 1][j] * q[i];}}double ans = 0;for (int j = (n + 1) / 2; j <= n; j++){ans += dp[n][j];}cout << fixed << setprecision(10) << ans;
}
J - Sushi
原题链接:J - Sushi
分析
我要补概率了。
正解
#include <bits/stdc++.h>
using namespace std;
const int N = 305;
double dp[N][N][N];
int cnt[5];
double n;
signed main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cin >> n;for (int i = 1; i <= n; i++){int tmp;cin >> tmp;cnt[tmp]++;}for (int k = 0; k <= n; k++){for (int j = 0; j <= n; j++){for (int i = 0; i <= n; i++){if (i)dp[i][j][k] += dp[i - 1][j][k] * i / (i + j + k + 0.0);if (j)dp[i][j][k] += dp[i + 1][j - 1][k] * j / (i + j + k + 0.0);if (k)dp[i][j][k] += dp[i][j + 1][k - 1] * k / (i + j + k + 0.0);if (i || j || k)dp[i][j][k] += n / (i + j + k + 0.0);}}}cout << fixed << setprecision(15) << dp[cnt[1]][cnt[2]][cnt[3]];
}
K - Stones
原题链接:K - Stones
分析
博……博弈论?这不是DP吗?
确实是 DP,甚至是背包的变种。我们考虑若一个人选了第个,那么必输。
所以我们设表示有i个石子后手/先手赢。那么,只有当
值可以更新对方。
那么,就可以写代码了。
黄题写不出来,我是**/(ㄒoㄒ)/~~
正解
#include <bits/stdc++.h>
using namespace std;
const int N = 105, K = 100005;
int n, k, a[N];
bool dp[K];
int main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cin >> n >> k;for (int i = 1; i <= n; i++){cin >> a[i];}for (int i = 1; i <= k; i++){for (int j = 1; j <= n; j++){if (i - a[j] >= 0){if (!dp[i - a[j]])dp[i] = 1;}}}if (dp[k])cout << "First";elsecout << "Second";
}
L - Deque
原题链接:L - Deque
分析
还在博弈/ll
所以,DP模拟博弈过程是我们需要掌握的。
设表示剩余区间
先手能取得的最大分差。
则,
正解
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 3005;
int n, a[N], f[N][N];
signed main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cin >> n;for (int i = 1; i <= n; i++){cin >> a[i];}for (int len = 1; len <= n; len++){for (int l = 1; l + len - 1 <= n; l++){int r = l + len - 1;if ((n - len) % 2 == 1){f[l][r] = min(f[l + 1][r] - a[l], f[l][r - 1] - a[r]);}else{f[l][r] = max(f[l + 1][r] + a[l], f[l][r - 1] + a[r]);}}}cout << f[1][n];
}
M - Candies
原题链接:M - Candies
分析
优化想不到可以理解,但是这么简单的状态没盯出来应该反思。
设表示考虑前i个孩子,恰好分完j个糖果的方案数。
考虑转移,,复杂度
.
考虑优化,前缀和!设
转移方程:
正解
#include <bits/stdc++.h>
#define int long long
#define mod 1000000007
using namespace std;
const int N = 105, K = 100005;
int n, k, a[N];
long long sum[N][K], dp[N][K];
signed main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cin >> n >> k;for (int i = 1; i <= n; i++){cin >> a[i];}dp[1][0] = sum[1][0] = 1;for (int i = 1; i <= k; i++){dp[1][i] = (i <= a[1]);sum[1][i] = dp[1][i] + sum[1][i - 1];}for (int i = 2; i <= n; i++){dp[i][0] = sum[i][0] = 1;for (int j = 1; j <= k; j++){if (j <= a[i])dp[i][j] = sum[i - 1][j] % mod;elsedp[i][j] = (sum[i - 1][j] - sum[i - 1][j - a[i] - 1] + mod) % mod;sum[i][j] = (sum[i][j - 1] + dp[i][j]) % mod;}}cout << dp[n][k];
}
N - Slimes
原题链接:N - Slimes
分析
AT版石子合并?甚至不是环形……好吧数组开小了。
正解
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 405;
int n, m[405];
long long sum[405], f[N][N];
signed main(){cin >> n;memset(f, 0x3f, sizeof(f));for (int i = 1; i <= n; i++){cin >> m[i];sum[i] = sum[i - 1] + m[i];f[i][i] = 0;} for (int len = 1; len < n; len++){for (int l = 1; l + len <= n; l++){int r = l + len;for (int k = l; k <= r; k++){f[l][r] = min(f[l][r], f[l][k] + f[k + 1][r]);}f[l][r] += sum[r] - sum[l - 1];}}cout << f[1][n];
}
P - Independent Set
原题链接:P - Independent Set
别问为啥没有O,蒟蒻没学状压DP,不会/ll,幸运的是,这周末有老师讲!
分析
树形DP,当我以为这题可以降黄被我切掉的时候,审题给我当头一棒!
Here, it is not allowed to paint two adjacent vertices both in black.
看成不允许相邻颜色相同了,我是**/(ㄒoㄒ)/~~
正解
#include <bits/stdc++.h>
#define int long long
#define mod 1000000007
using namespace std;
const int N = 100005;
int n, x, y;
vector<int> e[N];
int f[N][2];
void dfs(int u, int fa){f[u][0] = f[u][1] = 1;for (auto v : e[u]){if (v == fa)continue;dfs(v, u);f[u][0] = f[u][0] * ((f[v][0] + f[v][1]) % mod) % mod;f[u][1] = f[u][1] * f[v][0] % mod;}
}
signed main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cin >> n;for (int i = 1; i < n; i++){cin >> x >> y;e[x].push_back(y);e[y].push_back(x);}dfs(1, 0);cout << (f[1][0] + f[1][1]) % mod;
}
Q - Flowers
原题链接:Q - Flowers
分析
第一眼以为是单调栈优化DP,但细想一下,找到前面一个比它高度小的不见得价值足够大。所以,这是一道线段树/树状数组优化DP,该来的终究还是来了/ll
由于线段树通用性更好,所以我们写线段树。
我们设表示以第i个元素结尾的价值最大的子序列,
考虑转移,
线段树维护区间最大值即可。
正解
#include <bits/stdc++.h>
using namespace std;
const int N = 200005;
int n, h[N], a[N];
long long dp[N];
struct segtree{long long maxn[N << 2];void pushup(int p){maxn[p] = max(maxn[p << 1], maxn[p << 1 | 1]);}void modify(int p, int l, int r, int pos, long long k){if (l == r){maxn[p] = max(maxn[p], k);return ;}int mid = (l + r) >> 1;if (pos <= mid)modify(p << 1, l, mid, pos, k);elsemodify(p << 1 | 1, mid + 1, r, pos, k);pushup(p);}long long query(int p, int l, int r, int s, int t){if (s > t) return 0;if (s <= l && r <= t){return maxn[p];}int mid = (l + r) >> 1;long long ans = 0;if (s <= mid)ans = max(ans, query(p << 1, l, mid, s, t));if (t > mid)ans = max(ans, query(p << 1 | 1, mid + 1, r, s, t));return ans;}
}T;
int main(){ios::sync_with_stdio(0);cin.tie(0);cin >> n;for (int i = 1; i <= n; i++){cin >> h[i];}for (int i = 1; i <= n; i++){cin >> a[i];}for (int i = 1; i <= n; i++){long long maxn = T.query(1, 1, n, 1, h[i] - 1);dp[i] = maxn + a[i];T.modify(1, 1, n, h[i], dp[i]);}cout << T.query(1, 1, n, 1, n);return 0;
}