hdu3555 数位DP



     这个题算是数位DP的入门,第一次接触数位DP。自己没有解决出来,参看了好几位大神的博客。直接进入这个问题吧

     首先定义数组DP[i][3],含义如下:

     DP[i][0] : 表示最大长度为 i 的数字中不出现 “49”的数字总个数。

     DP[i][1]:表示最大长度为 i 的数字中不出现“49”,但是最高位是“9”的数字总个数。

     DP[i][2] 表示最大长度为 i 的数字中出现“49”的数字总个数。

题目给出的数字范围最大长度是 19 位(2^63-1).我们首先来计算DP[i][3].显然长度为 1 的数有:DP[1][0]=10,DP[1][1]=1,DP[1][2]=0;计算DP[i][3]的代码如下:

        dp[1][0] = 10;
	dp[1][1] = 1;
	dp[1][2] = 0;
	for (i = 2; i < 20; i++){
		dp[i][0] = dp[i - 1][0] * 10 - dp[i-1][1];  
		dp[i][1] = dp[i - 1][0];                   
		dp[i][2] = dp[i - 1][2] * 10 + dp[i - 1][1]; 
	}

以上公式:DP[i][0]=DP[i-1][0]×10 - DP[i-1][1]  ,这个公式的解释是这样的:在DP[i-1][0] 添加一位数字在最高位有0...9一共10中添加办法,但是当添加的数字是4,若是遇到DP[i-1][1]最高为是9的时候,就会构成“49”.所以需要减去。对于后面两个公式也是这样分析。这样就明白了DP[i][1]其实是DP[i][0]和DP[i][2]之间的纽带。计算完了DP[i][3],下面看看如何统计n.

    我们将数字n按从高位到低位来分区统计,比如 n=456:

              4 : 0 - 399

              5 : 400-449

              6 : 450 -455

   然后还有 456本身:

再如 n=5693,则分为:

              5 : 0 - 4999

              6 : 5000 - 5599

              9 : 5600 -5689

              3: 5690-5692

       然后 5693本身。

有一种情况就是数字 n 中本身就含有 “49” ,如 n= 149632,这时候需要特别处理。显然对于 149000 - 149632都是符合条件的数字。

#include<iostream>
using namespace std;
_int64 bit[20];
_int64 dp[20][3];
int main(){
	int i,T,len;
	_int64 n,ans;
	bool flag;
	dp[1][0] = 10;
	dp[1][1] = 1;
	dp[1][2] = 0;
	for (i = 2; i < 20; i++){
		dp[i][0] = dp[i - 1][0] * 10 - dp[i-1][1];  
		dp[i][1] = dp[i - 1][0];                   
		dp[i][2] = dp[i - 1][2] * 10 + dp[i - 1][1]; 
	}
	cin >> T;
	while (T--){
		ans = 0;
		len = 0;
		flag = 0;
		cin >> n;
		while (n){
			bit[++len] = n % 10;
			n /= 10;
		}
		bit[len + 1] = 0;
		for (i = len; i > 0; i--){
			ans += dp[i - 1][2] * bit[i];
			if (flag)
				ans += dp[i-1][0]*bit[i];
			if (!flag&&bit[i] > 4)
				ans += dp[i - 1][1];
			if (bit[i + 1] == 4 && bit[i] == 9)
				flag = 1;            //n中本身包含49
		}
		if (flag)
			ans++;
		cout << ans << endl;
	}
	return 0;
}


    

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值