poj2282(数位dp)

题目

这题题目比较好理解,就是统计a,b之间的数中数码0~9分别出现的次数

思路:

如果a>b不妨将两者互换顺序,这样就相当于a<=b了。然后该次数即1~b中数码0~9出现次数

减去1~a-1中数码0~9出现次数。

考虑某个数num,令v[i][j]表示该数前i位中j出现的次数,这个比较好算,预处理一下就行了

然后呢,用f[i][j]表示1~该数前i位表示的数中j出现的个数

则通常情况下f[i][j] = f[i - 1][j] * 10 + (value + 1) - v[i - 1][j]* (9 -val)  (其中value为前i-1位表示的数,val表示当前位的数值)

这个理解可以以1034中f[4][3]为例,f[3][3]即1~103中数码3出现次数,然后1~103后面加个数码0~9,显然1~102后面加个数码0~9,这些数值肯定小于1034,然后这些数值中3出现的次数即cnt[1~102][3]*10,即cnt[1~103][3]*10-1*5(103中出现数码3)一次,f[3][3]*10的话,1035~1039中3出现的次数被多考虑了要减掉,对应的即v[i-1][j]*(9-val)

至于value+1,是因为上面只考虑了各数前面n-1位中3出现的次数而未计算最后一位3出现的次数。+1是因为总的可能就一个值3,前缀可视为0。

当然,这只是通常情况,还有j为0的,以及一些还要减去一的就不再多说,主要还是草稿纸上写写然后程序调调比较清楚

代码如下:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

class Reader {
	static BufferedReader reader;
	static StringTokenizer tokenizer;

	static void init(InputStream input) {
		reader = new BufferedReader(new InputStreamReader(input));
		tokenizer = new StringTokenizer("");
	}

	static String next() throws IOException {
		while (!tokenizer.hasMoreTokens()) {
			tokenizer = new StringTokenizer(reader.readLine());
		}
		return tokenizer.nextToken();
	}

	static int nextInt() throws IOException {
		return Integer.parseInt(next());
	}
}

public class Main {

	/**
	 * @param args
	 */
	static int a, b, t, lena, lenb;
	static String stra, strb;
	static char cha[], chb[];
	static int va[][], vb[][], fa[][], fb[][];
	private static int dealnum(int num, int f[][], int v[][]) {
		String str = String.valueOf(num);
		char ch[] = str.toCharArray();
		int len = ch.length;
		for (int i = 1; i <= len; i++) {
			for (int j = 0; j <= 9; j++)
				v[i][j] = v[i - 1][j];
			v[i][ch[i - 1] - '0']++;
		}
		if (num == 0) return 1;
		for (int i = 0; i <= (ch[0] - '0'); i++)
			f[1][i] = 1;
		f[1][0]=0;
		int val;
		int value = ch[0] - '0';
		for (int i = 2; i <= len; i++) {
			val = ch[i - 1] - '0';
			for (int j = 0; j <= 9; j++)
				f[i][j] = f[i - 1][j] * 10 + (value + 1) - v[i - 1][j]
						* (9 -val);
			f[i][0]--;
			for (int j=val+1;j<=9;j++)
				f[i][j]--;
			value = value * 10 + val;
		}
		return len;
	}

	private static void deal() {
		if (a > b) {
			t = a;
			a = b;
			b = t;
		}
		a--;
		fa=new int[10][10];
		fb=new int[10][10];
		va=new int[10][10];
		vb=new int[10][10];
		lena=dealnum(a, fa, va);
		lenb=dealnum(b, fb, vb);
		for (int i=0;i<=9;i++)
			System.out.print((fb[lenb][i]-fa[lena][i])+" ");
		System.out.println();
	}

	public static void main(String[] args) throws IOException {
		// TODO Auto-generated method stub
		Reader.init(System.in);
		a = Reader.nextInt();
		b = Reader.nextInt();
		while (a != 0) {
			deal();
			a = Reader.nextInt();
			b = Reader.nextInt();
		}
	}

}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值