对于「数位 DP」题,都存在「询问 [ a , b ] [a, b] [a,b]( a a a 和 b b b 均为正整数,且 a < b a < b a<b)区间内符合条件的数值个数为多少」的一般形式,通常我们需要实现一个查询 [ 0 , x ] [0, x] [0,x] 有多少合法数值的函数 int dp(int x),然后应用「容斥原理」求解出 [ a , b ] [a, b] [a,b] 的个数: d p ( b ) − d p ( a − 1 ) dp(b) - dp(a - 1) dp(b)−dp(a−1)。 对于本题,虽然只需要求解 [ 0 , n ] [0, n] [0,n] 范围内数的个数,但其实拓展到求 [ a , b ] [a, b] [a,b] 区间个数的也不会增加难度。
具体的,对于「数位 DP」问题通常是「从高位到低位」的分情况讨论。
不失一般性的考虑数值 n n n 的某一位 c u r cur cur 是如何被处理的:
如果当前位 c u r = 1 cur = 1 cur=1 的话,由于我们需要满足「小于等于 n n n」的要求,因此如果该位填 0 0 0 的话,后面的低位填什么都是满足要求的,因此我们期望能够查表得出「长度为 i + 1 i + 1 i+1,且二进制位置 i i i 数值为 0 0 0 时」有多少合法数值,将其累加到答案中; 与此同时,我们需要确保当前位选 1 1 1 是合法的,即我们需要记录上一位 p r e v prev prev 是什么,确保 c u r cur cur 和 p r e v prev prev 不同时为 1 1 1。
如果当前位 c u r = 0 cur = 0 cur=0 的话,我们只能选 0 0 0,并决策下一位。
当出现「当前位无法填入 c u r cur cur」或者「决策到最低位」时,则完成了所有合法答案的统计。
至于流程 1 1 1 中的查表操作,我们可以使用 static 预处理出 f 数组,定义 f [ i ] [ j ] f[i][j] f[i][j] 为考虑二进制长度为 i i i,且最高位为 j j j( 0 0 0 or 1 1 1)时的合法数个数。
注意:为了防止重复计数问题,我们在不失一般性的计算 f [ i ] [ 0 ] f[i][0] f[i][0] 和 f [ i ] [ 1 ] f[i][1] f[i][1] 时,不能采用诸如 f [ i ] [ c u r ] + = f [ i − 1 ] [ p r e v ] f[i][cur] += f[i - 1][prev] f[i][cur]+=f[i−1][prev] 的 “后向查找依赖” 的方式进行转移,而要采用 f [ i + 1 ] [ c u r ] + = f [ i ] [ p r e v ] f[i + 1][cur] += f[i][prev] f[i+1][cur]+=f[i][prev] “前向主动更新” 的方式进行转移。
不失一般性的考虑 f [ i ] [ 0 ] f[i][0] f[i][0] 和 f [ i ] [ 1 ] f[i][1] f[i][1] 能够更新哪些状态:
classSolution { public: intminFallingPathSum(vector<vector<int>>& matrix){ if(matrix.size()<1) return0; int val = INT_MAX; for(int i=0;i<matrix[0].size();i++){ val = min(val,minFallingPathSum(matrix,0,i)); } return val; } intminFallingPathSum(vector<vector<int>>& m,int y,int x){ if(y>=m.size()) return0; if(x<0||x>=m[0].size()) return INT_MAX; int a = minFallingPathSum(m,y+1,x-1); int c = minFallingPathSum(m,y+1,x); int b = minFallingPathSum(m,y+1,x+1); int g = min(a,min(b,c)); if(g==INT_MAX) return INT_MAX; return g+m[y][x]; } };
动态规划
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
classSolution { public: intminFallingPathSum(vector<vector<int>>& matrix){ int m = matrix.size(),n=matrix[0].size(); vector<vector<int>> dp(m,vector<int>(n,0)); for(int x=0;x<n;++x) dp[m-1][x] = matrix[m-1][x]; for(int y=m-2;y>=0;--y){ for(int x=1;x<n-1;++x){ int val = dp[y+1][x]; if(x-1>=0) val = min(val,dp[y+1][x-1]); if(x+1<n) val = min(val,dp[y+1][x+1]); dp[y][x] = matrix[y][x]+val; } } int res = INT_MAX; for(int x=0;x<n;++x) res = min(res,dp[0][x]); return res; } };