refactor(P7074): 重构动态规划解法并优化路径计算逻辑

重构了P7074问题的解决方案,将原来的二维DP扩展为三维状态表示,以更精确地跟踪不同移动方向的最大路径和。新增了对特殊情况的处理(如单格、单列情况),并添加了详细注释说明算法逻辑。

同时清理了test.cpp文件中的冗余代码,仅保留main函数框架。
This commit is contained in:
Zengtudor 2025-11-02 14:40:52 +08:00
parent 4e419a46c7
commit 9541f97f8b
3 changed files with 95 additions and 200 deletions

94
src/10/31/P7074.cpp Normal file
View File

@ -0,0 +1,94 @@
#include <iostream>
#include <vector>
#include <algorithm>
// 使用 long long 防止整数溢出,因为路径和可能很大
using ll = long long;
// 定义一个非常小的数作为负无穷大用于初始化dp数组
// 路径和可能为负所以不能用0初始化
const ll INF = -1e18; // 1000*1000*10000 = 10^10ll可以存下
int main() {
// 提高C++ IO效率
std::ios_base::sync_with_stdio(false);
std::cin.tie(NULL);
int n, m;
std::cin >> n >> m;
std::vector<std::vector<int>> a(n + 1, std::vector<int>(m + 1));
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
std::cin >> a[i][j];
}
}
// dp[i][j][k] 表示到达格子 (i, j) 的最大路径和
// k = 0: 最后一步是从左边 (i, j-1) 来的
// k = 1: 最后一步是从上面 (i-1, j) 来的 (向下移动)
// k = 2: 最后一步是从下面 (i+1, j) 来的 (向上移动)
std::vector<std::vector<std::vector<ll>>> dp(n + 2, std::vector<std::vector<ll>>(m + 2, std::vector<ll>(3, INF)));
// 起点 (1, 1) 初始化
// 到达 (1, 1) 本身,路径和就是 a[1][1]
// 逻辑上可以认为是从 (1, 0) 这个虚拟格子过来的
dp[1][1][0] = a[1][1];
// DP过程逐列计算
for (int j = 1; j <= m; ++j) {
// Step 1: 从左边 (j-1) 列转移到当前 (j) 列
if (j > 1) {
for (int i = 1; i <= n; ++i) {
// 从 (i, j-1) 点可以向右到达 (i, j)
// (i, j-1) 可能是从左/上/下三个方向到达的,取其中最大值
ll from_left = std::max({dp[i][j-1][0], dp[i][j-1][1], dp[i][j-1][2]});
// 如果前一格的所有状态都不可达,则当前状态也无法通过此路径到达
if (from_left != INF) {
dp[i][j][0] = from_left + a[i][j];
}
}
}
// Step 2: 在当前列 j 内,进行向上/向下的更新
// 从上往下更新
for (int i = 2; i <= n; ++i) {
// 要到达 (i, j) 且最后一步是向下,必须从 (i-1, j) 过来
// 在 (i-1, j),不能是从 (i, j) 往下再往上,所以不考虑 dp[i-1][j][2]
ll from_up = std::max(dp[i-1][j][0], dp[i-1][j][1]);
if (from_up != INF) {
// 当前状态 dp[i][j][1] 可能已经被之前的计算更新过(或者没有)
// 需要和从 (i-1,j) 走过来的新路径和比较,取最大值
dp[i][j][1] = std::max(dp[i][j][1], from_up + a[i][j]);
}
}
// 从下往上更新
for (int i = n - 1; i >= 1; --i) {
// 要到达 (i, j) 且最后一步是向上,必须从 (i+1, j) 过来
// 在 (i+1, j),不能是从 (i, j) 往上再往下,所以不考虑 dp[i+1][j][1]
ll from_down = std::max(dp[i+1][j][0], dp[i+1][j][2]);
if (from_down != INF) {
dp[i][j][2] = std::max(dp[i][j][2], from_down + a[i][j]);
}
}
}
// 终点是 (n, m)。到达该点的路径可能是从左边 (n, m-1) 或上面 (n-1, m) 来的。
// 所以取 dp[n][m][0] 和 dp[n][m][1] 的较大值。
// dp[n][m][2] 是从 (n+1, m) 来的不合法其值应为INF。
ll ans = std::max(dp[n][m][0], dp[n][m][1]);
// 对于只有一个格子的特殊情况(1x1)
if (n == 1 && m == 1) {
ans = a[1][1];
} else if (m == 1) { // 只有一列,只能往下走
// 只有一列时没有从左边来的转移dp[i][1][0]只在i=1时有值
// dp[i][1][1] 是从上往下累加的
ans = dp[n][1][1];
}
std::cout << ans << std::endl;
return 0;
}

View File

@ -1,47 +0,0 @@
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
const long long INF = -1e18;
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
int n, m;
cin >> n >> m;
vector<vector<int>> a(n + 1, vector<int>(m + 1));
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
cin >> a[i][j];
}
}
vector<vector<long long>> dp(n + 2, vector<long long>(m + 2, INF));
dp[1][0] = 0;
for (int j = 1; j <= m; ++j) {
for (int i = 1; i <= n; ++i) {
dp[i][j] = dp[i][j - 1] + a[i][j];
}
for (int i = 2; i <= n; ++i) {
dp[i][j] = max(dp[i][j], dp[i - 1][j] + a[i][j]);
}
for (int i = n - 1; i >= 1; --i) {
dp[i][j] = max(dp[i][j], dp[i + 1][j] + a[i][j]);
}
}
cout << dp[n][m] << endl;
return 0;
}

View File

@ -1,155 +1,3 @@
#include <bits/stdc++.h>
#define int long long
using namespace std;
struct node
{
int date = 0;
int lan = 0;
int left = 0;
int right = 0;
int lanc = 0;
};
int n, q, m;
vector<int> a;
vector<node> tree;
void build(vector<node> &tree, vector<int> &a, int rt, int left, int right)
{
if(left == right)
{
tree[rt].left = left;
tree[rt].right = right;
tree[rt].date = a[left] % 38;
tree[rt].lan = 0;
tree[rt].lanc = 1;
return;
}
int mid = left + (right - left) / 2;
build(tree, a, rt * 2, left, mid);
build(tree, a, rt * 2 + 1, mid + 1, right);
tree[rt].left = left;
tree[rt].right = right;
tree[rt].lan = 0;
tree[rt].lanc = 1;
tree[rt].date = (tree[rt * 2].date + tree[rt * 2 + 1].date) % 38;
}
void lazycl(vector<node> &tree, int rt)
{
if(tree[rt].lan > 0 or tree[rt].lanc != 1)
{
int laz = tree[rt].lan / (tree[rt].right - tree[rt].left + 1);
tree[rt * 2].date += (laz * ((tree[rt * 2].right - tree[rt * 2].left) + 1)) % 38;
tree[rt * 2].lan += laz * ((tree[rt * 2].right - tree[rt * 2].left) + 1);
tree[rt * 2].date = (tree[rt * 2].date * tree[rt].lanc) % 38;
tree[rt * 2].lanc *= tree[rt].lanc;
tree[rt * 2 + 1].date += (laz * ((tree[rt * 2 + 1].right - tree[rt * 2 + 1].left) + 1)) % 38;
tree[rt * 2 + 1].lan += laz * ((tree[rt * 2 + 1].right - tree[rt * 2 + 1].left) + 1);
tree[rt * 2 + 1].date = (tree[rt * 2 + 1].date * tree[rt].lanc) % 38;
tree[rt * 2 + 1].lanc *= tree[rt].lanc;
tree[rt].lan = 0;
tree[rt].lanc = 1;
}
}
void qjadd(vector<node> &tree, int rt, int add, int cl, int cr, int left, int right)
{
lazycl(tree,rt);
if(cl > right or cr < left)
{
return;
}
if(cl <= left and cr >= right)
{
tree[rt].date += (add * (tree[rt].right - tree[rt].left + 1)) % 38;
tree[rt].lan += add * (tree[rt].right - tree[rt].left + 1);
return;
}
int mid = left + (right - left) / 2;
qjadd(tree, rt * 2, add, cl, cr, tree[rt].left, mid);
qjadd(tree, rt * 2 + 1, add, cl, cr, mid + 1, tree[rt].right);
tree[rt].date = (tree[rt * 2].date + tree[rt * 2 + 1].date) % 38;
}
void qjmul(vector<node> &tree, int rt, int mul, int cl, int cr, int left, int right)
{
int main(){
if(cl > right or cr < left)
{
return;
}
if(cl <= left and cr >= right)
{
tree[rt].date *= (mul * tree[rt].lanc) % 38;
tree[rt].lanc = mul * tree[rt].lanc;
//cout << tree[rt].date << " [" << tree[rt].left << "," << tree[rt].right << "]" << tree[rt].lan << " " << tree[rt].lanc << endl;
return;
}lazycl(tree,rt);
int mid = left + (right - left) / 2;
qjmul(tree, rt * 2, mul, cl, cr, tree[rt].left, mid);
qjmul(tree, rt * 2 + 1, mul, cl, cr, mid + 1, tree[rt].right);
tree[rt].date = (tree[rt * 2].date + tree[rt * 2 + 1].date) % 38;
//cout << tree[rt].date << " [" << tree[rt].left << "," << tree[rt].right << "]" << tree[rt].lan << " " << tree[rt].lanc << endl;
}
void qjcx(vector<node> &tree, int rt, int cl, int cr, int left, int right, int &sum)
{
lazycl(tree, rt);
if(cl > right or cr < left)
{
return;
}
if(cl <= left and cr >= right)
{
sum += tree[rt].date % 38;
//cout << tree[rt].date << " [" << tree[rt].left << "," << tree[rt].right << "]" << tree[rt].lan << " " << tree[rt].lanc << endl;
return;
}
int mid = left + (right - left) / 2;
qjcx(tree, rt * 2, cl, cr, tree[rt].left, mid, sum);
qjcx(tree, rt * 2 + 1, cl, cr, mid + 1, tree[rt].right, sum);
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n >> q >> m;
a.resize(n + 1);
tree.resize((n + 1) * 4);
for(int i = 1;i <= n; i++)
{
cin >> a[i];
}
build(tree, a, 1, 1, n);
// for(int i = 1;i <= n * 4; i++)
// {
// cout << tree[i].date << " [" << tree[i].left << "," << tree[i].right << "]" << tree[i].lan << " " << tree[i].lanc << endl;
// }
for(int i = 1;i <= q;i ++)
{
int c, x, y;
cin >> c >> x >> y;
if(c == 1)
{
int k;
cin >> k;
qjmul(tree, 1, k % 38, x, y, 1, n);
}
else if(c == 2)
{
int k;
cin >> k;
qjadd(tree, 1, k % 38, x, y, 1, n);
}
else
{
int sum = 0;
qjcx(tree, 1, x, y, 1, n, sum);
cout << sum % 38 << endl;
}
}
}