ABC344
基本情况
ABCE 秒了,D小细节处理出错(太久没写dp)+4。
A - Spoiler
有更优雅的解法
signed main(){
std::string s;
std::cin >> s;
std::cout << s.substr(0, s.find("|")) + s.substr(s.rfind("|") + 1) << '\n';
}
D - String Bags
分组背包,但是字符串的细节要注意
signed main()
{
std::cin.tie(nullptr)->sync_with_stdio(false);
std::string s; std::cin >> s; int M = SZ(s);
int N; std::cin >> N;
const int MAX = std::numeric_limits<int>::max();
std::vector<int> dp(M + 1, MAX); dp[0] = 0;
int ans = MAX;
for (int g = 1; g <= N; g++) {
int A; std::cin >> A;
auto ndp = dp;//这里是因为上一轮的被更新过的状态需要沿用到这一轮,否则下面直接min就不是最优
for (int i = 0; i < A; i++) {
std::string t; std::cin >> t;
for (int j = 0; j + SZ(t) - 1 < M; j++) {
if (s.substr(j, SZ(t)) == t and dp[j] != MAX) {
ndp[j + SZ(t)] = std::min(ndp[j + SZ(t)], dp[j] + 1);
}
}
}
dp = ndp;
}
if (dp[M] == MAX) {
std::cout << -1 << '\n';
} else {
std::cout << dp[M] << '\n';
}
return 0;
}
E - Insert or Erase
思路很直接,用 \(map\) 映射双链表的下标即可。
\(MyCode\)
signed main() {
std::map<int, int> l, r;
r[0] = INT_MAX;
l[INT_MAX] = 0;
int n, q;
std::cin >> n;
auto add = [&](int x, int y) -> void {//把y插入到x右边
r[y] = r[x];
l[r[x]] = y;
r[x] = y;
l[y] = x;
};
auto del = [&](int x) -> void {//删除x元素
r[l[x]] = r[x];
l[r[x]] = l[x];
};
for (int i = 0, x = 0, y; i < n; i++) {//读入初始元素
std::cin >> y;
add(x, y);
x = y;
}
std::cin >> q;
for (int i = 0, op, x, y; i < q; i++) {
std::cin >> op;
if (op == 1) {
std::cin >> x >> y;
add(x, y);
}
else {
std::cin >> x;
del(x);
}
}
for (int x = r[0]; x != INT_MAX; x = r[x]) {
std::cout << x << ' ';
}
return 0;
}
\(\texttt{STLcode}\)
signed main()
{
std::cin.tie(nullptr)->sync_with_stdio(false);
int N; std::cin >> N;
std::vector<int> A(N); for (auto& x : A) {std::cin >> x;}
std::list<int> L(A.begin(), A.end());
std::map<int, std::list<int>::iterator> val_iter;
for (auto it = L.begin(); it != L.end(); it++) {val_iter[*it] = it;}
int Q; std::cin >> Q; while (Q--) {
int opt; std::cin >> opt;
if (opt == 1) {
int x, y; std::cin >> x >> y;
auto it = std::next(val_iter[x]);//因为insert是在左端插入
val_iter[y] = L.insert(it, y);
} else {
int x; std::cin >> x; L.erase(val_iter[x]);
}
}
for (auto& x : L) {std::cout << x << ' ';} std::cout << '\n';
return 0;
}
F - Earn to Advance
https://atcoder.jp/contests/abc344/tasks/abc344_f
贪心简化DP
可以将“通过 \(P_{i,j}\) 增加金钱”的操作替换为“通过迄今为止经过的所有格子中的最大 \(P_{i,j}\) 来增加金钱”,而不改变答案。
因此,可以假设只有在需要时才赚钱;这样一来,一旦确定了一条路径,最优过程也就唯一确定了。
特别地,我们只需记住“迄今为止经过的所有格子中的最大 \(P_{i,j}\)”,因此可以设计这个状态:
\[dp_{i, j, x, y}表示从 (1, 1) 到 (i, j), 其中最大的 P 为 P_{x, y} 的情况下的最小步数(单指停留步)和最小花费。
\]
signed main()
{
std::cin.tie(nullptr)->sync_with_stdio(false);
int N; std::cin >> N;
std::vector P(N, std::vector<int>(N)), R(N, std::vector<int>(N - 1)), D(N - 1, std::vector<int>(N));;
auto read = [&](auto& V) {for (auto& vec : V) for (auto& x : vec) {std::cin >> x;};}; read(P); read(R); read(D);
//dp[i][j][x][y] 表示从 (1, 1) 到 (i, j), 其中最大的 P 为 P(x, y) 的情况下的最小步数(单指停留步)和最小花费。
std::vector dp(N, std::vector(N, std::vector(N, std::vector(N, std::make_pair(inf, inf)))));
dp[0][0][0][0] = {0, 0};
for (int i = 0; i < N; i++) for (int j = 0; j < N; j++) for (int x = 0; x <= i; x++) for (int y = 0; y <= j; y++) {
if (j + 1 < N) {//向右走
auto res = dp[i][j][x][y]; auto&[step, cost] = res;
cost += R[i][j];
if (cost > 0) {
i64 add_step = (cost - 1) / P[x][y] + 1;//向下取整后再加一,算出到达当前这个花费,至少要停多少步
step += add_step; cost -= add_step * P[x][y];//增加步数,减去花费
}
dp[i][j + 1][x][y] = std::min(dp[i][j + 1][x][y], res); dp[i][j + 1][i][j + 1] = std::min(dp[i][j + 1][i][j + 1], res);
}
if (i + 1 < N) {
auto res = dp[i][j][x][y]; auto&[step, cost] = res;
cost += D[i][j];
if (cost > 0) {
i64 add_step = (cost - 1) / P[x][y] + 1;
step += add_step; cost -= add_step * P[x][y];
}
dp[i + 1][j][x][y] = std::min(dp[i + 1][j][x][y], res); dp[i + 1][j][i + 1][j] = std::min(dp[i + 1][j][i + 1][j], res);
}
}
std::cout << dp[N - 1][N - 1][N - 1][N - 1].first + 2 * (N - 1) << '\n';//停留的花费,加上(1, 1)到(n, n)的曼哈顿距离
return 0;
}