你的赛时代码:
这份代码的方向和连续步数已经放进了节点:
其中:
* ld 表示最后移动方向;
* t 表示当前方向连续移动次数。
这部分状态更新是正确的。主要错误出在 vmp 的剪枝。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
一、致命错误:VMP 只记录坐标,没有记录方向状态
你定义:
并使用:
这相当于认为:
> 到达同一个格子时,只要价值较小,就一定不如价值较大的路径。
这是错误的。
虽然两条路径到达同一个格子,但它们的:
* 最后方向可能不同;
* 连续步数可能不同;
* 后续能够选择的方向也可能不同。
较大价值的路径可能因为连续步数已经达到 KKK 而无法继续,较小价值的路径反而能够到达终点。
反例
因为 K=1K=1K=1,不能连续两次走相同方向。
正确路径是:
路径价值为:
1+1+1+1=41+1+1+1=4 1+1+1+1=4
但程序会先处理从起点向右的状态:
到达 (2,2)(2,2)(2,2) 时:
* 价值为 333;
* 最后方向是向下;
* 已经连续向下走了 111 步;
* 因为 K=1K=1K=1,不能再向下走到终点。
此时:
随后另一条路径:
也到达 (2,2)(2,2)(2,2):
* 价值同样为 333;
* 最后方向是向右;
* 下一步可以向下到达终点。
但你的条件是:
即:
不成立。
因此这个真正能够到达终点的状态被删除,程序最终输出:
正确答案应该是:
所以不能使用:
正确状态应该是:
表示到达 (x,y)(x,y)(x,y)、最后方向为 dir、当前连续走了 step 步时的最大价值。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
二、权值为零时,合法路径可能完全无法入队
vmp 是全局数组,初始值全部为 000:
但转移条件写的是:
如果路径价值恰好为 000,就不能进入该格子。
反例
从 (1,1)(1,1)(1,1) 向右一步就可以到达终点,正确答案为:
程序开始时:
判断:
不成立,因此终点不会入队,最终输出:
即使暂时不考虑方向状态,vmp 也至少应该初始化为 -1 或负无穷,而不能初始化为 000。
但是只修改初始化仍然不能解决第一个根本问题。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
三、即使价值相等,不同状态也不能删除
你的条件使用严格大于:
这会直接删除价值相等的路径。
但价值相等不代表状态相同。
例如上面的反例中,两条路径到达 (2,2)(2,2)(2,2) 的价值都为 333,但是:
* 一条最后向下,不能继续向下;
* 另一条最后向右,可以继续向下。
所以不能仅仅把 > 改为 >=。
改成 >= 虽然可能保留部分等价值状态,但仍然无法正确区分:
甚至会产生大量重复入队。
根本解决方法仍然是:
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
四、当前队列中会存在大量失效节点
当某个格子的 vmp 被更新时,之前已经进入队列的较小价值节点不会被删除。
例如:
之后又出现更大的价值:
那么队列中两个节点都会继续扩展。
这不会自动导致答案错误,因为节点自身保存了 v、ld 和 t,但会产生许多重复搜索。
而且当前的二维 vmp:
* 有时错误删除有用状态;
* 有时又保留大量旧节点。
因此它既不能正确剪枝,也不能保证良好复杂度。
本题只向右和向下移动,天然是一个有向无环图,不需要 BFS。直接按照行列顺序做 DP 更清晰。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
五、连续步数的更新是正确的
你的判断:
表示:
* 如果继续相同方向;
* 且连续步数会超过 KKK;
* 就禁止这次移动。
这是正确的。
更新:
也正确:
* 方向相同:连续步数加一;
* 方向改变:连续步数重置为一。
初始状态:
虽然把初始方向写成了 0,但初始连续次数为 000:
* 第一次向右得到 t=1t=1t=1;
* 第一次向下也得到 t=1t=1t=1。
所以这里不会导致错误。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
六、起点就是终点时能够正确处理
如果:
起点入队后立即满足:
于是:
能够正确输出:
这部分没有问题。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
七、数据类型没有问题
一条路径最多经过:
N+M−1≤999N+M-1\le 999 N+M−1≤999
个格子,每个格子最大为 10410^4104,最大总价值不超过:
999×104=9,990,000999\times 10^4=9,990,000 999×104=9,990,000
因此:
都不会溢出。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
总结
这份代码主要有两个会直接导致错误的问题:
1. vmp[x][y] 只按照坐标保留最大值,错误删除不同方向、不同连续步数的有用状态。
2. vmp 初始化为 000,导致总价值为 000 的合法路径无法进入队列。
正确状态必须是:
其中:
* x,y:当前位置;
* dir:最后移动方向;
* step:当前方向连续移动次数;
* dp:该状态下能够获得的最大价值。
最关键的错误行就是:
因为是否应该保留一个状态,不能只比较它在这个格子上的路径价值,还必须同时考虑最后方向和连续步数。