数位DP小结
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数位DP小结相关的知识,希望对你有一定的参考价值。
数位DP,本质上来说还是DP。只不过DP的定义和数字的每一位有关。
这里简单的讲解两道题的思路,抛砖迎玉,作为数位DP的入门。
第一题,http://acm.hdu.edu.cn/showproblem.php?pid=2089 不要62和4
题目大意:不吉利的数字为所有含有4或62的号码。例如:
62315 73418 88914
都属于不吉利号码。但是,61152虽然含有6和2,但不是62连号,所以不属于不吉利数字之列。
你的任务是,对于每次给出的一个牌照区间号,推断出交管局今次又要实际上给多少辆新的士车上牌照了。
import java.util.Scanner; public class Main { public static int[][] dp; public static void main(String[] args) { dp = new int[7][10]; getdp(); Scanner in = new Scanner(System.in); int m = in.nextInt(); int n = in.nextInt(); while (!(m == 0 && n == 0)) { System.out.println(helper(n + 1) - helper(m)); m = in.nextInt(); n = in.nextInt(); } in.close(); return; } public static void getdp() { dp[0][0] = 1; for (int i = 1; i < 7; i++) { for (int j = 0; j < 10; j++) { if (j == 4) { dp[i][j] = 0; } else if (j == 6) { for (int k = 0; k < 10; k ++) { dp[i][j] += dp[i - 1][k]; } dp[i][j] -= dp[i - 1][2]; } else { for (int k = 0; k < 10; k ++) { dp[i][j] += dp[i - 1][k]; } } } } } public static int helper(int x) { int[] a = new int[8]; a[0] = 0;//a[0] 用来存储index while (x > 0) { a[++a[0]] = x % 10; x /= 10; } a[7] = 0; //车牌一共6位,a[7]补0,为了后面写法方便 int res = 0; for (int i = a[0]; i >= 1; i--) { for (int j = 0; i < a[i]; j++) { if (j != 4 && !(a[i + 1] == 6 && j == 2)) { res += dp[i][j]; } } if (a[i] == 4) { break; } if (a[i + 1] == 6 && a[i] == 2) { break; } } return res; } }
第二题。http://hihocoder.com/problemset/problem/1520?sid=1111176
描述
小Hi有一张纸条,上面写着一个长度为N的整数。由于年代过于久远,其中有些位置已经看不清了,我们用‘?‘来代替这个位置。小Hi印象中记得这个数字除以X的余数为Y,他想知道这个数最小可能是多少?
注意这个整数的首位不能是0,除非它本身等于0。
输入
第1行:1个长度为N的字符串,只包含数字0~9和‘?‘,1≤N≤200
第2行:两个整数,X和Y,1≤X≤200,000,0≤Y<X
输出
第1行:若存在解,则输出最小可能的数字,否则输出No solution
思路: 和上一题类似,暴力枚举是显然不行的,这里的数最高长达200位,2^200次方的枚举量,显然是不能AC的。
这个题和余数有关,所以思考建立数组,保存余数相关信息,进行dp。最简单的就是进行记忆化搜索,在暴力枚举的同时,记录枚举到当前位的已知余数是多少,如果已经遍历过,则可以直接跳过,这样可以节省许多计算量。
定义,dp[i][j] 表示,从最高位开始遍历的第i位的时候,余数是j的情况,是否遍历过。(遍历过则说明,第i位之后的所有位,不管取任何值都不会有满足题意的解,否则已经退出枚举直接输出结果了。)
没有转移方程。
初始化所有dp都是 false
举例说明思路,例如输入时1??00??00
如果第1个问号取0,第二个问号也取0,后两个问号,无论如何都不能取到满足条件的解。那么dp[3][j]=true;
下一次,再到第3位的时候,如果此时前三位计算得到的余数还是j电话,那么就不用再计算后面的几位的,因为无论取什么值,也不会有满足余数条件的解。这样就可以直接第三位取下一个值来判断了。
(这个题还用到了一些其他的方法来减少计算量,比如,预先计算一个数组expRem[i],用来保存10^i % X的余数。其实就是利用了mod运算的一些性质,来简化运算。)
代码如下:
import java.util.Scanner; //import java.util.*; public class Main { public static StringBuilder numbers; public static boolean[][] dp; public static int[] expRem; public static int X; public static int Y; public static int N; public static void main(String[] args) { // TODO Auto-generated method stub Scanner in = new Scanner(System.in); numbers = new StringBuilder(in.nextLine()); X = in.nextInt(); Y = in.nextInt(); N = numbers.length(); in.close(); dp = new boolean[N][X]; expRem = new int[N]; boolean exist_unknow = false; for (int i = 0; i < N; i++) { if (numbers.charAt(i) == ‘?‘) { exist_unknow = true; break; } } expRem[N - 1] = 1; for(int i = N - 2; i >= 0;i--){ expRem[i] = expRem[i + 1] * 10 % X; } //System.out.print(exist_unknow); if (!exist_unknow) { for(int i = 0; i < N; i++) { Y=(Y - expRem[i] * (numbers.charAt(i) -‘0‘) % X + X )% X; } if(Y==0) { System.out.print(numbers); }else { System.out.print("No solution"); } return; } if(N == 1) { for(int i = 0; i < 10; i++) { if(i % X == Y) { System.out.print(i); return; } } System.out.print("No solution"); return; } if(dfs(0,Y)){ }else { System.out.print("No solution"); } return; } public static boolean dfs(int depth, int target) { if (depth == N) { if (target == 0) { System.out.print(numbers); return true; } else { return false; } } if (dp[depth][target]) { return dp[depth][target]; } if (numbers.charAt(depth) != ‘?‘) { int newTarget = (target - expRem[depth] * (numbers.charAt(depth) - ‘0‘) % X + X) % X; dp[depth][target] = dfs(depth + 1, newTarget); return dp[depth][target]; } dp[depth][target] = false; int index = 0; if (depth == 0) { index = 1; } for (; index <= 9; index++) { char newChar = (char) (‘0‘ + index); numbers.setCharAt(depth, newChar); int newTarget = (target - expRem[depth] * (numbers.charAt(depth) - ‘0‘) % X + X) % X; if (dfs(depth + 1, newTarget)) { dp[depth][target] = true; return dp[depth][target]; } } numbers.setCharAt(depth, ‘?‘); return dp[depth][target]; } }
以上是关于数位DP小结的主要内容,如果未能解决你的问题,请参考以下文章