洛谷P1006&NOIP2008 传纸条

本文介绍了一个关于在二维网格中同时传递两张纸条的问题,并通过四维动态规划求解最大价值。该问题利用了状态转移方程,考虑了不同路径组合的可能性。

初见此题有点蒙蔽,然而可以发现n,m<=50,所以四维DP即可

来回两次可以等效成同时传两个纸条,

f[a][b][c][d]=max(f[a-1][b][c-1][d],f[a-1][b][c][d-1]),f[a][b-1][c][d-1],f[a][b-1][c-1][d])+g[a][b]+g[c][d];

#include<cstring>
#include<cstdio>
#include<fstream>
using namespace std;
int max(int a,int b){return a>b?a:b;}
int m,n,g[55][55]={0},f[55][55][55][55]={0};
int main(){
    //freopen("message.in","r",stdin);
    //freopen("message.out","w",stdout);
    scanf("%d%d",&m,&n);
    for(int i=1;i<=m;i++)
        for(int j=1;j<=n;j++)
            scanf("%d",&g[i][j]);
    for(int a=1;a<=m;a++)
        for(int b=1;b<=n;b++)
            for(int c=1;c<=m;c++)
                for(int d=1;d<=n;d++){
                    f[a][b][c][d]=max(max(f[a-1][b][c-1][d],f[a-1][b][c][d-1]),max(f[a][b-1][c][d-1],f[a][b-1][c-1][d]))+g[a][b]+g[c][d];
                    if(a==c&&b==d)f[a][b][c][d]-=g[a][b];
                }
    printf("%d",f[m][n][m][n]);
}

在这段代码中,四重 `for` 循环(嵌套循环)的核心作用是**实现动态规划(Dynamic Programming, DP)的状态转移过程**,用于求解一个经典的“双路径最大和”问题——即:两个人从网格的左上角 (1,1) 出发,走到右下角 (N,N),每次只能向下或向右走,路径上的数字可以被收集,但如果两人走到同一个格子,该格子的值只能被计算一次。目标是使两人路径所收集的总值最大。 下面是对这段代码逐层解析其功能与作用: --- ### 🔹整体结构分析 ```c for (int x1 = 1; x1 <= N; x1++) { for (int y1 = 1; y1 <= N; y1++) { for (int x2 = 1; x2 <= N; x2++) { for (int y2 = 1; y2 <= N; y2++) { ... } } } } ``` - 这是一个四维状态遍历: - `(x1, y1)` 表示第一个人当前的位置; - `(x2, y2)` 表示第二个人当前的位置。 - 因此,`dp[x1][y1][x2][y2]` 表示当两人分别走到 `(x1,y1)` 和 `(x2,y2)` 时,所能获得的最大分数。 - 所有状态按照坐标递增顺序枚举(因为只能向右或向下走),保证在计算当前状态时,所有可能的前驱状态已经计算完成。 --- ### 🔹状态值计算:`value` ```c int value; if (x1 == x2 && y1 == y2) { value = grid[x1][y1]; // 同一格子,只取一次 } else { value = grid[x1][y1] + grid[x2][y2]; // 不同格子,都取 } ``` - 当两个人位于**同一个格子**时,这个格子的价值只能被加一次(避免重复采集); - 否则,两个格子的价值都可以累加。 > ✅ 这是处理“共享资源”的关键逻辑,防止重复计数。 --- ### 🔹前驱状态转移:`prev` ```c int prev = 0; if (x1 > 1 && x2 > 1) prev = max(prev, dp[x1-1][y1][x2-1][y2]); if (x1 > 1 && y2 > 1) prev = max(prev, dp[x1-1][y1][x2][y2-1]); if (y1 > 1 && x2 > 1) prev = max(prev, dp[x1][y1-1][x2-1][y2]); if (y1 > 1 && y2 > 1) prev = max(prev, dp[x1][y1-1][x2][y2-1]); ``` - 每个人每步只能由上方或左方移动而来(仅允许向右/下走); - 所以 `(x1,y1)` 可能来自 `(x1−1,y1)` 或 `(x1,y1−1)`; - 同理 `(x2,y2)` 可能来自 `(x2−1,y2)` 或 `(x2,y2−1)`; - 组合起来就有四种可能的前驱状态: 1. 两人都从上面来:`(x1−1,y1), (x2−1,y2)` 2. 第一人从上、第二人从左:`(x1−1,y1), (x2,y2−1)` 3. 第一人从左、第二人从上:`(x1,y1−1), (x2−1,y2)` 4. 两人都从左边来:`(x1,y1−1), (x2,y2−1)` - 对这四个合法的前驱状态取最大值作为 `prev`,表示到达当前状态之前的最优得分。 > ✅ 这部分实现了 **DP 状态转移方程中的“来源选择”**,确保每一步都是最优路径的延续。 --- ### 🔹更新当前状态 ```c dp[x1][y1][x2][y2] = prev + value; ``` - 将前驱最优值 `prev` 加上当前获得的价值 `value`,得到当前状态的最大得分。 - 完成了状态定义的填充。 --- ### 🔹最终结果 程序最后输出: ```c printf("%d\n", dp[N][N][N][N]); ``` 即两人都走到终点 `(N,N)` 时的最高总分。 --- ### 🧩 总结:这段代码的功能和作用 | 功能 | 说明 | |------|------| | **问题建模** | 使用四维 DP 模拟两路径同时行进的过程 | | **状态设计** | `dp[x1][y1][x2][y2]` 表示两人分别在指定位置时的最大得分 | | **去重机制** | 若两人在同一格,则只加一次数值 | | **状态转移** | 枚举四种可能的前驱状态,取最大值进行转移 | | **正确性保障** | 循环顺序符合拓扑序(从小到大),确保依赖状态已计算 | 这是典型的“双人走路”类 DP 问题(如洛谷 P1006 纸条NOIP 提高组真题),通过将两个独立路径合并为联合状态进行优化求解。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值