G.Longest Path
拓扑排序dp
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int dp[100010], d[100010];
void solve() {
int n, m; scanf("%d%d", &n, &m);
vector<int> g[n + 1];
for (int i = 1;i <= m;i++) {
int a, b;
scanf("%d%d", &a, &b);
g[a].push_back(b);
d[b] ++;
}
queue<int> q;
for (int i = 1;i <= n;i++) if (!d[i]) q.push(i);
while (!q.empty()) {
int x = q.front(); q.pop();
for (auto y : g[x]) {
if (dp[x] + 1 > dp[y]) {
dp[y] = dp[x] + 1;
}
d[y] --;
if (!d[y]) q.push(y);
}
}
printf("%d\n", *max_element(dp + 1, dp + n + 1));
}
int main() {
solve();
return 0;
}
I.Coins
\(dp_{i,j}\)表示,当前枚举到第\(i\)个硬笔,有\(j\)个硬币正面向上的概率。
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
constexpr int mod = 1e9 + 7;
long double dp[3010][3010];
void solve() {
int n; cin >> n;
vector<long double> p(n + 1);
for (int i = 1;i <= n;i++) cin >> p[i];
dp[1][0] = 1.0 - p[1];
dp[1][1] = p[1];
for (int i = 2;i <= n;i++) {
for (int j = 0;j <= i;j++) {
dp[i][j] = dp[i - 1][j] * (1.0 - p[i]);
if (j > 0) dp[i][j] += dp[i - 1][j - 1] * p[i];
}
}
long double ans = 0;
for (int i = n / 2 + 1;i <= n;i++) ans += dp[n][i];
cout << fixed << setprecision(10) << ans << endl;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
solve();
return 0;
}
K.Stones
博弈论中,如果一个状态能够转移到必败态,则该状态为必胜态。
预处理出所有的必败态,由必败态进行状态转移。
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int dp[100010], vis[100010];
int a[110];
void solve() {
int n, k; cin >> n >> k;
set<int> s;
for (int i = 0;i < n;i++) {
int x; cin >> x;
s.insert(x);
dp[x] = 1;
vis[x] = 1;
a[i] = x;
}
for (int i = 0;i < *s.begin();i++) dp[i] = 0, vis[i] = 1;
for (int i = 0;i <= k;i++) {
if (vis[i]) continue;
vis[i] = 1;
for (auto v : s) {
if (i - v >= 0) {
dp[i] |= (dp[i - v] == 0 ? 1 : 0);
}
else break;
}
}
cout << (dp[k] == 1 ? "First\n" : "Second\n");
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
solve();
}
L.Deque
两个人都想要自己的得分最大化。
记\(dp_{l,r}\)为在\(l-r\)这一段进行游戏的最终\(x-y\)的大小。
那么我们可以类似搜索,用区间\(dp\)求解。
#include <bits/stdc++.h>
#define pb push_back
#define endl '\n'
using namespace std;
using ll = long long;
ll dp[3010][3010];
void solve() {
int n; cin >> n;
vector<ll> a(n + 1);
memset(dp, -0x3f, sizeof dp);
for (int i = 1; i <= n;i++) {
cin >> a[i];
dp[i][i] = a[i];
}
for (int len = 2;len <= n;len ++) {
for (int l = 1;l <= n;l ++) {
int r = l + len - 1;
dp[l][r] = max(a[l] - dp[l + 1][r], a[r] - dp[l][r - 1]);
}
}
cout << dp[1][n] << endl;
}
signed main(void) {
cin.tie(nullptr), cout.tie(nullptr) -> ios::sync_with_stdio(false);
solve();
return 0;
}
M.Candies
记\(dp_{i,j}\)表示到第i个人,分j份糖果的方案数。
但是我们需要用前缀和优化转移方程。
#include <bits/stdc++.h>
#define endl '\n'
using namespace std;
using ll = long long;
constexpr int mod = 1e9 + 7;
ll dp[110][100010], pre[110][100010];
void solve() {
int n, k; cin >> n >> k;
vector<int> a(n + 1);
ll sum = 0;
for (int i = 1;i <= n;i++ ) {
cin >> a[i];
sum += 1ll * a[i];
}
if (sum < k && k != 0) {
cout << 0 << endl;
return;
}
for (int i = 0;i <= k;i++) pre[0][i] = 1;
for (int i = 1;i <= n;i++) pre[i][0] = 1;
for (int i = 1;i <= n;i++) {
for (int j = 0;j <= k;j++) {
int r = min(j, a[i]);
dp[i][j] = pre[i - 1][j];
if (j - r - 1 >= 0) dp[i][j] = (dp[i][j] - pre[i - 1][j - r - 1] + mod) % mod;
}
for (int j = 1;j <= k;j++) pre[i][j] = (dp[i][j] + pre[i][j - 1]) % mod;
}
cout << (dp[n][k] + mod) % mod << endl;
}
signed main(void) {
cin.tie(nullptr), cout.tie(nullptr) -> ios::sync_with_stdio(false);
solve();
return 0;
}
M.Matching
状态压缩dp
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
constexpr int mod = 1e9 + 7;
int dp[23][1 << 23];
void solve() {
int n; cin >> n;
vector<int> have(n + 1);
for (int i = 1;i <= n;i++) {
for (int j = 0;j < n;j++) {
int x; cin >> x;
if (x == 1) have[i] |= (1 << j);
}
}
dp[0][0] = 1;
for (int i = 1;i <= n;i++) {
for (int j = 0;j < (1 << n);j++) {
if (__builtin_popcount(j) == i - 1) {
for (int k = 0;k < n;k++) {
if ((have[i] >> k & 1) and (j >> k & 1) == 0) {
dp[i][j | (1 << k)] = (dp[i][j | (1 << k)] + dp[i - 1][j]) % mod;
}
}
}
}
}
cout << dp[n][(1 << n) - 1] << '\n';
}
signed main(void) {
cin.tie(nullptr), cout.tie(nullptr)->ios::sync_with_stdio(false);
solve();
return 0;
}
P.Independent Set
类似没有上司的舞会,树形dp。
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
constexpr int mod = 1e9 + 7;
ll dp[100010][2];
void solve() {
int n; cin >> n;
vector<vector<int>> g(n + 1);
for (int i = 0; i < n - 1;i++) {
int u, v; cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
auto dfs = [&](auto self, auto &g, auto x, auto fa)->void {
dp[x][0] = dp[x][1] = 1;
for (auto y : g[x]) {
if (y == fa) continue;
self(self, g, y, x);
dp[x][0] = dp[x][0] * (dp[y][0] + dp[y][1]) % mod;
dp[x][1] = dp[x][1] * dp[y][0] % mod;
}
return;
};
dfs(dfs, g, 1, -1);
cout << (dp[1][0] + dp[1][1]) % mod << endl;
}
signed main(void) {
cin.tie(nullptr), cout.tie(nullptr)->ios::sync_with_stdio(false);
solve();
return 0;
}
Q.Flowers
树状数组优化状态转移。
由于我们的dp值大小只会增大,所以可以用树状数组维护前缀最大值。
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
constexpr int mod = 1e9 + 7;
constexpr int N = 200010;
int n;
ll dp[200010], c[N];
void add(int x, ll d) {
while ( x <= n) {
c[x] = max(c[x], d);
x += x & -x;
}
}
ll query(int x) {
ll res = 0;
while (x > 0) {
res = max(res, c[x]);
x -= x & -x;
}
return res;
}
void solve() {
cin >> n;
vector<int> a(n + 1), h(n + 1);
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++) {
ll res = query(h[i] - 1);
dp[i] = max(dp[i], res + a[i]);
add(h[i], dp[i]);
}
cout << *max_element(dp + 1, dp + n + 1) << endl;
}
signed main(void) {
cin.tie(nullptr), cout.tie(nullptr)->ios::sync_with_stdio(false);
solve();
return 0;
}
标签:Atcoder,Educational,Contest,int,ll,long,solve,using,dp
From: https://www.cnblogs.com/coldarra/p/16799974.html