使矩阵满足条件的最少操作次数
题目
输入 \(m\times n\) 的矩阵 \(grid\),输出需要的最少操作次数,使得每列的值相等,且相邻两列的值不相等。每次操作可以将任意单元格的值修改为任意非负整数。
数据范围:\(1\leq n,m\leq 1000\),\(0\leq grid[i][j]\leq 9\)。
思路
首先,每列的值肯定是修改为 \([0,9]\) 之间更优,因为操作次数可能更少。我们可以从前往后枚举每列修改为什么值,可以发现存在重叠子问题。定义 \(dp[i][j]\) 表示将第 \(i\) 列修改为 \(j\),使得 \([0,i]\) 列满足条件所需的最少操作次数。有状态转移方程 \(dp[i][j]=\min{(dp[i-1][k])}+cnt_{i,j}\),其中 \(0\leq k\leq 9\) 且 \(k\neq j\),\(cnt_{i,j}\) 表示将第 \(i\) 列修改为 \(j\) 所需的操作次数。时间复杂度为 \(O(mn+nU^{2})\),空间复杂度为 \(O(nU)\),其中 \(U\) 表示 \(grid[i][j]\) 的值域大小。灵神题解提到一个优化方式,状态转移只会从前面所有列操作的最优值或者次优值转移过来,利用这个特性可以降低时间和空间复杂度。
代码
1 | class Solution { |
最短路径中的边
题目
输入整数 \(n\) 和长度为 \(m\) 的数组 \(edges\),表示包含 \(n\) 个节点和 \(m\) 条边的无向带权图(无重边和自环)。输出长度为 \(m\) 的布尔数组 \(ans\),如果从节点 \(0\) 到节点 \(n-1\) 的所有最短路中包含 \(edges[i]\),则 \(ans[i]=true\)。
数据范围:\(2\leq n\leq 5\times 10^{4}\),\(1\leq m\leq\min{(5\times 10^{4},\frac{n(n-1)}{2})}\),\(1\leq w_{i}\leq 10^{5}\)。
思路
首先使用 Dijkstra 算法求出从节点 \(0\) 到所有其他节点的最短路,然后暴力的想法是以节点 \(0\) 为起点使用 DFS 回溯来遍历图,遍历的同时存储路径上的边,当到达节点 \(n-1\) 的路径长度等于最短路时,则将该路径上的边都置为 \(true\)。回溯理论上最坏情况下的时间复杂度为 \(O(n\times n!)\),但是可以通过力扣的测试,因为可以利用最短路的限制进行剪枝,至于能否构造出 Hack 回溯的测试用例,我不太清楚。
正确的解法是使用两次 Dijkstra 算法,分别求出从节点 \(0\) 到所有其他节点的最短路,和从节点 \(n-1\) 到所有其他节点的最短路。然后枚举每一条边,判断路径长度 \(d_{0,u}+w_{u,v}+d_{v,n-1}\) 或者 \(d_{0,v}+w_{u,v}+d_{u,n-1}\) 是否等于最短路,从而可以得出该边是否在某个最短路中。
另一种做法是只对起点使用一次 Dijkstra 算法,然后从终点反向 DFS,经过的边如果满足 \(d_{0,v}+w_{u,v}=d_{0,u}\),则说明该边在某个最短路上。