主要实现思路
整体流程思路 :
程序旨在解决一个模拟现实场景中接馅饼的问题,给定多组馅饼掉落的位置和时间信息,要计算出在特定规则(每秒只能在移动不超过一米的范围内接住馅饼且初始站在位置 5)下,最多能接到的馅饼数量。整体通过循环读取多组输入数据,并针对每组数据运用动态规划的思想进行计算,最后输出每组数据对应的最多能接到的馅饼数量。 具体步骤思路 :
初始化阶段 :
首先定义了一个二维数组 dp,用于在动态规划过程中存储中间结果,其第一维表示馅饼掉落的位置(经过一定偏移处理,便于边界等情况的考虑),第二维表示时间,dp[w][t] 大致代表在位置 w、时间 t 时能接到的馅饼数量相关的统计值。同时定义了一个简单的求最大值函数 max,用于后续方便地比较并取两个整数中的较大值。 在 main 函数中,通过 while (scanf("%d", &n)!= EOF && n) 循环不断读取输入的馅饼个数 n,只要 n 不为 0 且没到文件末尾,就对每组输入数据进行处理。对于每组数据:
先将变量 tt(用于记录最大时间)初始化为 0,然后使用 memset(dp, 0, sizeof(dp)); 将 dp 数组的所有元素初始化为 0,确保每次处理新数据时数据初始状态干净。 接着通过 while (n--) 循环读取 n 组馅饼掉落的位置 w 和时间 t,在读取过程中,对位置 w 进行 +1 偏移操作后(可能是为了更好地处理边界等情况),将 dp[w + 1][t] 的值加 1(因为同一位置同一时间可能掉落多个馅饼,通过累加来记录数量),同时调用 max 函数更新 tt,使其始终记录着当前组输入数据中出现的最大时间。 动态规划计算阶段 :
采用逆向动态规划的方式,从最大时间 tt 的前一个时间开始(通过 for (i = tt - 1; i >= 0; i--) 循环),倒序向前遍历每个时间点,目的是根据后面时间的已知结果推导出前面时间能接到的最多馅饼数。 对于每个时间点 i,再通过 for (j = 1; j <= 11; j++) 循环遍历每个可能的位置(对应 dp 数组第二维索引范围经过调整后的情况,代表实际的馅饼掉落位置范围)。 在这个双重循环中,按照动态规划的核心逻辑,通过状态转移方程 dp[j][i] = max(dp[j][i + 1], max(dp[j + 1][i + 1], dp[j - 1][i + 1])) + dp[j][i]; 更新 dp[j][i] 的值,也就是当前位置 j 在当前时间 i 能接到的最多馅饼数,等于当前位置在当前时间的原有值(dp[j][i])加上其相邻三个位置(上、下、同位置)在下一时刻(i + 1)能接到的最多馅饼数中的最大值,这体现了每秒只能在移动不超过一米的范围内接住馅饼这个规则约束,通过不断更新 dp 数组的值,逐步推导出前面时间各位置能接到的最多馅饼数。 结果输出阶段 :
当完成对一组输入数据的动态规划计算后,通过 printf("%d\n", dp[6][0]); 输出最终结果,这里选择输出位置为 6(经过偏移调整后的对应原问题中 gameboy 初始站在 5 这个位置附近的关键中间位置)且时间为 0(最初时刻)时的 dp 值,这个值就代表了在给定的馅饼掉落情况和规则下,gameboy 最多能接到的馅饼数量。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 定义二维数组dp,用于动态规划存储中间结果,dp[w][t]表示在位置w(经过简单偏移处理,实际对应馅饼掉落位置)、时间t时能接到的馅饼数量的相关统计值
// 第一维大小为14是考虑到位置范围可能的偏移及边界情况等,第二维大小为100001对应时间范围(最大时间上限)
int dp[14][100001];
// 定义函数max,用于返回两个整数中的较大值,这是一个简单的比较函数,用于后续取最大值操作
int max(int m, int n)
{
return m > n ? m : n;
}
int main()
{
// n用于存储输入的馅饼个数,i、j作为循环计数变量,tt用于记录出现的最大时间,w用于存储馅饼掉落的位置,t用于存储馅饼掉落的时间
int n, i, j, tt, w, t;
// 循环读取输入,只要通过scanf读取到的n不为0且没到文件末尾(EOF),就持续处理输入数据(可以处理多组测试数据)
while (scanf("%d", &n) != EOF && n)
{
// 初始化最大时间tt为0,后续会更新为实际输入数据中出现的最大时间
tt = 0;
// 将二维数组dp的所有元素初始化为0,确保每次处理新的测试数据时数据初始状态正确,避免之前的数据干扰
memset(dp, 0, sizeof(dp));
// 循环读取n组馅饼掉落的位置和时间信息
while (n--)
{
// 读取一个馅饼掉落的位置w和时间t
scanf("%d%d", &w, &t);
// 由于原代码中位置范围处理的关系(可能为了方便边界处理等),对位置w进行了 +1 的偏移操作,将对应位置的dp数组元素值加1,表示该位置在该时间有馅饼掉落(同一位置同一时间可能掉落多个馅饼,通过累加计数)
dp[w + 1][t]++;
// 更新最大时间tt,取当前读取的时间t和已记录的最大时间tt中的较大值,确保tt始终记录着所有输入数据中的最大时间
tt = max(tt, t);
}
// 从最大时间tt的前一个时间开始,倒序向前遍历每个时间点(逆向动态规划,从后往前推导前面时间能接到的最多馅饼数)
for (i = tt - 1; i >= 0; i--)
{
// 遍历每个可能的位置(对应数组dp的第二维索引范围,这里是1到11,对应实际问题中的馅饼掉落位置范围经过偏移调整后的情况)
for (j = 1; j <= 11; j++)
{
// 通过动态规划的状态转移方程更新dp[j][i]的值,它等于当前位置在当前时间的原有值(dp[j][i])加上以下三个相邻位置(上、下、同位置)在下一时刻(i + 1)能接到的最多馅饼数中的最大值
// 这里的逻辑是考虑gameboy每秒只能在移动不超过一米的范围内接住馅饼,所以当前时间能接到的最多馅饼数要参考相邻位置在下一时刻的情况来综合确定
dp[j][i] = max(dp[j][i + 1], max(dp[j + 1][i + 1], dp[j - 1][i + 1])) + dp[j][i];
}
}
// 输出最终结果,即位置为6(经过偏移调整后的实际中间位置,对应原问题中gameboy初始站在5这个位置能接到的最多馅饼情况)且时间为0(最初时刻)时的dp值,这个值就代表gameboy最多能接到的馅饼数量
printf("%d\n", dp[6][0]);
}
return 0;
}