蓝桥杯每日一题-----数位dp练习

题目

蓝桥杯每日一题-----数位dp练习_第1张图片
链接

参考代码

写了两个,一个是很久以前写的,一个是最近刚写的,很久以前写的时候还不会数位dp所以写了比较详细的注释,这两个代码主要是设置了不同的记忆数组,通过这两个代码可以理解记忆数组设置的灵活性。


import java.util.Scanner;



public class Main {
	// 给定两个正整数a和b,求在[a,b]中的所有整数中,每个数码(digit)各出现了多少次。
	static int[] b = new int[15];
	static long[][][] f = new long[15][10][15];

	public static void main(String[] args) {
		Scanner scanner = new Scanner(System.in);
		long n = scanner.nextLong();
		long m = scanner.nextLong();
		for (int i = 0; i < 15; i++) {
			for (int j = 0; j < 10; j++) {
				for (int j2 = 0; j2 < 15; j2++) {
					f[i][j][j2] = -1;
				}
			}
		}
		for (int i = 0; i <= 9; i++) {
			System.out.print((get(m, i) - get(n - 1, i)) + " ");
		}

	}

	private static long get(long x, int target) {
		// TODO Auto-generated method stub
		long t = x;
		int i = 0;
		while (t > 0) {
			b[i++] = (int) (t % 10);
			t = t / 10;
		}
		return dfs(target, true, true, i - 1, 0);
	}

//	target  表示要计算的值
//	e  是否贴上界
//当要判断的数的位数小于原数的位数时,就没有上界了,如果位数和原数一样,每一位的上界就是对应的原数
//	first  是否是数的第一个位
//	k  数的第几位 now
//  t  出现的次数
	private static long dfs(int target, boolean e, boolean first, int k, int t) {
		// TODO Auto-generated method stub
		//

		long res = 0;
		if (k == -1)
			return t;// 如果数位考虑完,返回出现的次数
		// f[k][target][t]时公用的,找这个x的时候可以用,找下一个x的时候也可以用,所以他应该具有普遍性,
		// 但是当我的位数和x一样时,他有不同的边界
		if ((!e) && (!first) && f[k][target][t] != -1)
			return f[k][target][t];
		// 判断这一位我可以最大填到哪个数
		int u = e ? b[k] : 9;
		// 如果是要填写首位数,那么这个数等于0的时候其实位数是减一,要单独考虑。
		if (first) {
			res += dfs(target, false, true, k - 1, t);
		}
		// 注意我已经判断了如果要给首位填数,首位数为0的情况,所以当要给首位填数时,我要从1开始,如果不是首位,那么从0开始即可
		for (int i = first ? 1 : 0; i <= u; i++) {
			// 贴上界是指此时的位数的上界是受此位的数的限制,最大数可能到达不了9
			// 只有本位是贴上界的下一位才有贴上界的可能,比如54321,只要是第五位他的上界就是5,也就是此位最大取到5,
			// 这也就是为什么在get中一开始遍历的时候e = true
			// 当确定了这个数是5位的时候,如果第五位小于5,那他后面的数是不贴上界的最大值可以写到9,但如果第五位等于5
			// 那下一位也是贴上界的,最大值只能取到4
			// (i == u) & e 这也是它的来源
			res += dfs(target, (i == u) & e, false, k - 1, (i == target) ? t + 1 : t);
		}
		// f[k][target][t]时公用的,找这个x的时候可以用,找下一个x的时候也可以用,所以他应该具有普遍性,
		// 但是当我的位数和x一样时,他有不同的边界限制,此时他得出的值具有个性,不能给其他的x用,所以不能存在f数组中
		// 这是为什么要判断是否贴上界的原因
		// 当该位数为一个数的首位时,因为此时该数的实际位数是不确定的,首位为0时实际位数会减少,但我依然记录在res中,这样此时的
		// (f[k][target][t] 就有两种情况一是k是首位的时候,二是k不是首位的时候,所以我们应该舍去k时首位的时候
		if (!e && !first) {
			f[k][target][t] = res;
		}
		return res;
	}
}

import java.util.Arrays;
import java.util.Scanner;

public class 数字计数 {
	static long dp[][][][] = new long[15][10][15][2];
public static void main(String[] args) {
	Scanner scanner = new Scanner(System.in);
	long a = scanner.nextLong();
	long b = scanner.nextLong();
	for(int i = 0;i < 15;i++)
		for(int j = 0;j < 10;j++)
			for(int k = 0;k < 15;k++)
				Arrays.fill(dp[i][j][k], -1);
	for(int i = 0;i < 10;i++)
	   System.out.print(solve(b,i)-solve(a-1,i)+" ");//20 21 2 12 22 32 42 52 62 72 82 92 23 24 25 26 27 28 29
//	System.out.println(solve(b, 2));//2 12 20-29(10) 32 42 52 62 72 82 92 22
}
static int nums[] = new int[15];
static int temp[] = new int[15];
static int tot = 0;
private static long solve(long n,int k) {
	// TODO Auto-generated method stub
	tot=0;
	while(n > 0) {
		nums[++tot] = (int) (n % 10);
		n /= 10;
	}
	return dfs(tot,1,1,k,0);
}
private static long dfs(int cnt, int limit, int zeros, int k, int num) {
	// TODO Auto-generated method stub
	if (cnt==0) {
//		for(int i = 1;i <= 2;i++) System.out.print(temp[i]);
//		System.out.println( " "+ num);
		return num;
	}
	if(limit==0&&dp[cnt][k][num][zeros]!=-1) return dp[cnt][k][num][zeros];
	int up = limit==1?nums[cnt]:9;
	int st = zeros==1?1:0;
	long res = 0;
	if(zeros==1)  res+=dfs(cnt-1, (0==up?1:0)&limit, 1, k, num);//该位填0
	for(int i = st;i <= up;i++) {
//		temp[cnt]=i;
		res+=dfs(cnt-1, (i==up?1:0)&limit, 0, k, i==k?num+1:num);
	}
	if(limit==0) dp[cnt][k][num][zeros]=res;
	return res;
}
}

你可能感兴趣的:(蓝桥杯,算法,深度优先)