日常刷题2025-3-2
1.日常刷题2025-3-162.日常训练2025-1-23.日常训练2025-1-34.日常训练2025-1-55.日常训练2025-1-86.日常训练2025-1-117.日常训练2025-1-128.日常训练2025-1-139.日常训练2025-1-1410.日常训练2025-1-1511.日常训练2025-1-1612.日常训练2025-1-1713.日常训练2025-1-1814.日常训练2025-1-1915.日常训练2025-1-2116.日常训练2025-1-2217.日常刷题2025-1-2318.日常训练2025-1-2419.日常刷题2025-1-2520.日常刷题21.日常刷题2025-2-622.日常刷题2025-2-923.日常刷题2025-2-1424.日常刷题2025-2-1525.日常刷题2025-2-1726.日常刷题2025-2-2027.日常刷题2025-2-2128.日常刷题2025-2-2229.日常刷题2025-2-2430.日常刷题2025-2-2631.日常刷题2025-2-2732.日常刷题2025-2-2833.日常刷题2025-3-1
34.日常刷题2025-3-2
35.日常刷题2025-3-336.日常刷题2025-3-537.日常刷题2025-3-638.日常刷题2025-3-739.日常刷题2025-3-840.日常刷题2025-3-941.日常刷题2025-3-1042.日常刷题2023-3-1143.日常刷题2025-3-1344.非常棒的二分和DP日常刷题2025-3-2
P1006 [NOIP 2008 提高组] 传纸条
绿色
思路:棋盘格DP
从头到尾一来一回其实和从头开始走两次是等效的,我们就当作从头开始走两次思考就好。此题难点在于如何解决重复点的问题。
两条路径不能有重复点,则一定是一条在上面,一条在下面,所以当上面的路径走到
时,下面一条路径只能在 i + 1 行 1 ~ j - 1 列选择。
代码:四维DP
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; #define INF = 0x3f3f3f3f int a[51][51], f[51][51][51][51]; int m, n; int max(int i, int j, int k, int l){ int m = max(i, j), n = max(k, l); return max(m, n); } int main() { cin >> m >> n; for(int i = 1; i <= m; i++){ for(int j = 1; j <= n; j++) { cin >> a[i][j]; } } f[1][1][1][1] = 0; //garbage for(int i = 1; i <= m; i++){ for(int j = 1; j <= n; j++){ for(int k = i+1; k <= m; k++){ for(int l = 1; l < j; l++){ f[i][j][k][l] = max( f[i][j-1][k][l-1], f[i][j-1][k-1][l], f[i-1][j][k-1][l], f[i-1][j][k][l-1] )+a[i][j]+a[k][l]; } } } } cout << f[m-1][n][m][n-1] << endl; return 0; }
代码2:判断重复的写法
为啥只需要把两条路径走到相同点的状态标记成无意义就可以让整个状态转移过程中都不会走到重复的点?
因为DP本质是对重叠子问题用相同的方法反复求解。写DP状态转移时只需要关注一个阶段,就能让所以阶段都按照相同的方式转移。所以把重复点的状态标记成无意义是针对全局所有转移的,这样只要是遇到重复点的状态都不会使用,进而保证了最终答案中就没有用到重复点的状态。
#include <bits/stdc++.h> using namespace std; #define int long long int a[60][60]; int dp[60][60][60][60]; signed main() { ios::sync_with_stdio(false); ios_base::sync_with_stdio(false); cin.tie(0), cout.tie(0); int m, n; cin >> m >> n; for (int i = 1; i <= m; i++) { for (int j = 1; j <= n; j++) { cin >> a[i][j]; } } for (int i = 1; i <= m; i++) { for (int j = 1; j <= n; j++) { for (int k = 1; k <= m; k++) { for (int l = 1; l <= n; l++) { dp[i][j][k][l] = max(dp[i - 1][j][k - 1][l], dp[i - 1][j][k][l - 1]); dp[i][j][k][l] = max(dp[i][j - 1][k - 1][l], max(dp[i][j][k][l], dp[i][j - 1][k][l - 1])); dp[i][j][k][l] += a[i][j] + a[k][l]; if (i == k && j == l && !(i == 1 && j == 1 || i == m && j == n)) { dp[i][j][k][l] = -114514; } } } } } cout << dp[m][n][m][n]; return 0; }
经验+1
std::string 的读入是按照空格终止的!!!
D - Pigeon Swap
绿色
思路:小巧思
暴力做法肯定不行。
本题的难点就是如何在不用暴力模拟的情况下,又能有办法来表示交换两个巢穴,且保证鸽子的指针不受影响。如果鸽子的指针指向巢的标签,那么在交换巢时难免要去一个个更改鸽子的指针,显然是不可行的。为了避免一个个的修改鸽子的指针,我们就在加一个变量,来表示巢的位置,当发生交换时,不再交换巢的标签,而是交换巢的位置。有了位置后,标签就成了维持鸽子指针不变的中间变量,所以答案肯定不再是这里的标签,而是会因为交换产生变化的位置。
#include <bits/stdc++.h> typedef std::pair<long long, long long> pll; typedef std::pair<int, int> pii; #define INF 0x3f3f3f3f #define MOD 998244353 using i64 = long long; const int N = 1e5+5; void solve(){ int n, q; std::cin >> n >> q; std::vector<int> nest(n), pos(n), id(n); std::iota(nest.begin(), nest.end(), 0); pos = id = nest; while (q--){ int op; std::cin >> op; if (op == 1){ int a, b; std::cin >> a >> b; a--, b--; nest[a] = id[b]; }else if (op == 2){ int a, b; std::cin >> a >> b; a--, b--; pos[id[a]] = b; pos[id[b]] = a; std::swap(id[a], id[b]); }else if (op == 3){ int a; std::cin >> a; a--; std::cout << pos[nest[a]] + 1 << '\n'; } } } signed main() { std::ios::sync_with_stdio(false); std::cin.tie(nullptr); std::cout<<std::setiosflags(std::ios::fixed)<<std::setprecision(2); int t = 1, i; for (i = 0; i < t; i++){ solve(); } return 0; }
本文作者:califeee
本文链接:https://www.cnblogs.com/califeee/p/18745829
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步