C. Cycle Graph?
如果图满足连通且所有点的度数为 \(2\),那么就是环图!
代码实现
#include <bits/stdc++.h>
#include <atcoder/all>
using namespace atcoder;
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
int main() {
int n, m;
cin >> n >> m;
vector<int> deg(n);
dsu uf(n);
rep(i, m) {
int a, b;
cin >> a >> b;
--a; --b;
deg[a]++;
deg[b]++;
uf.merge(a, b);
}
if (deg == vector<int>(n, 2) and uf.size(0) == n) {
puts("Yes");
}
else puts("No");
return 0;
}
D. Goin' to the Zoo
对每个动物园做三进制枚举
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using ll = long long;
int main() {
int n, m;
cin >> n >> m;
vector<int> c(n);
rep(i, n) cin >> c[i];
vector<vector<int>> a(m);
rep(i, m) {
int k;
cin >> k;
a[i] = vector<int>(k);
rep(j, k) cin >> a[i][j], a[i][j]--;
}
vector<int> p3(n+1, 1);
rep(i, n) p3[i+1] = p3[i]*3;
const ll INF = 1e18;
ll ans = INF;
rep(s, p3[n]) {
vector<int> t(n);
rep(i, n) t[i] = s/p3[i]%3;
ll cost = 0;
rep(i, n) cost += c[i]*t[i];
rep(j, m) {
int cnt = 0;
for (int i : a[j]) cnt += t[i];
if (cnt < 2) cost = INF;
}
ans = min(ans, cost);
}
cout << ans << '\n';
return 0;
}
E. Bowls and Beans
不需要将一碗豆子分散移动,也不需要越过其他豆子。也就是说,从右边的豆子开始,依次“合并到左边相邻的豆子所在的碗中”是最优的!剩下的就是最短路问题了!
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
int solve(vector<int> c) {
int n = c.size();
const int INF = 1001001001;
vector<int> dp(n+1, INF);
dp[0] = 0;
rep(i, n) {
int now = INF;
rep(j, c[i]) {
if (0 <= i-j) now = min(now, dp[i-j]);
}
dp[i+1] = now+1;
}
return dp[n];
}
int main() {
int n;
cin >> n;
vector<int> c(n), a(n);
rep(i, n-1) cin >> c[i+1];
rep(i, n-1) cin >> a[i+1];
a[0] = 1;
int ans = 0;
vector<int> nc;
for (int i = 1; i < n; ++i) {
nc.push_back(c[i]);
if (a[i]) {
ans += solve(nc);
nc = vector<int>();
}
}
cout << ans << '\n';
return 0;
}
这里的 \(N\) 的范围比较小,即使是暴力dp跑的也很快
如果 \(N\) 再开大点,可以用线段树来优化
代码实现
const int INF = 1001001001;
int op(int a, int b) { return min(a, b); }
int e() { return INF; }
int solve(vector<int> c) {
int n = c.size();
segtree<int, op, e> dp(n+1);
dp.set(0, 0);
rep(i, n) {
int l = max(0, i-c[i]+1), r = i+1;
int now = dp.prod(l, r);
dp.set(i+1, now+1);
}
return dp.get(n);
}
F. Lost and Pound
概率dp
记 dp[i][j] 表示到第 \(i\) 次操作为止已经按下 \(j\) 次获胜按钮的状态继续游戏时的胜率
然后从后往前进行转移即可
对于转移,还需要开一个辅助dp
记 dp2[i][j] 表示到 \(c_i\) 为止总共按了 \(j\) 次按钮时的胜率的总和的最大值
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
inline void chmax(double& a, double b) { if (a < b) a = b; }
int main() {
int n, t, m, k;
cin >> n >> t >> m >> k;
vector dp(t+1, vector<double>(k+1));
dp[t][k] = 1;
for (int ti = t-1; ti >= 0; --ti) {
rep(kj, k+1) {
double now = 0;
{
vector dp2(m+1, vector<double>(m+1));
rep(i, m)rep(j, m+1) {
for (int c = 1; j+c <= m; ++c) {
chmax(dp2[i+1][j+c], dp2[i][j]+dp[ti+1][min(k, kj+c)]);
}
}
rep(i, m+1) {
if (i > n) break;
double x = dp2[i][m];
x += dp[ti+1][kj]*(n-i);
chmax(now, x);
}
}
dp[ti][kj] = now/n;
}
}
double ans = dp[0][0];
printf("%.10f\n", ans);
return 0;
}
G. Specified Range Sums
差分约束板题
令 \(C_i = \sum\limits_{k=1}^i A_k\)
那么 \(S = A_l + \cdots + A_r = C_r - C_{l-1}\)
\(\Rightarrow\) \(C_r - C_{l-1} \geqslant S\) 且 \(C_r - C_{l-1}
\leqslant S\)
\(A_i\) 是正整数 \(\Rightarrow\) \(C_i - C_{i-1} \geqslant 1\)
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using ll = long long;
int main() {
int n, m;
cin >> n >> m;
vector<tuple<int, int, int>> edges;
rep(i, n) edges.emplace_back(i+1, i, -1);
rep(i, m) {
int l, r, s;
cin >> l >> r >> s;
--l;
edges.emplace_back(r, l, -s);
edges.emplace_back(l, r, s);
}
const ll INF = 1e18;
vector<ll> dist(n+1, INF);
dist[n] = 0;
bool upd = true;
rep(ti, n+1) {
upd = false;
for (auto [a, b, c] : edges) {
ll nd = dist[a]+c;
if (dist[b] > nd) {
upd = true;
dist[b] = nd;
}
}
}
if (upd) puts("-1");
else cout << -dist[0] << '\n';
return 0;
}
浙公网安备 33010602011771号