HDU2089 数位DP

    原问题是求区间 [ n, m ]中,不含数字4和62的数的总个数.采用的办法是数位 DP。定义数组 dp[i][2],他的含义如下:

 dp[i][0] : 表示数字最大长度是 i 且不含4和62的数总个数。

 dp[i][1] : 表示数字的最大长度是 i 且不含 4 和62,但是最高位是2的数总个数。

于是:dp[i][0]=dp[i-1][0]*9-dp[i-1][1];

           dp[i][1]=dp[i-1][0];

计算 dp[i][2]代码如下:

memset(dp, 0, sizeof(dp));
	dp[0][0] =1;
	for (i = 1; i <10; i++){
		dp[i][0] = dp[i - 1][0] * 9 - dp[i - 1][1];
		dp[i][1] = dp[i - 1][0];
	}

完成 了上一步。我们将区间 [n,m]分为两部分来计算:即是计算[0,n]和[0,m],然后相减来的到最后结果(端点需要特殊处理)。同样的将数字 x分区间统计,如 x =598,则分为:

     5:  0 - 499

     9:  500-589

     8:  590-597

然后还有598本身。

注意一种情况,如果数字 x本身含有62或者4,则62或者4后面就没有满足要求的数字了。比如x=584169.则584后面的就没有满足要求的数字了。

#include<iostream>
using namespace std;
int dp[10][2];
int bit[12];
int Compute(int x,bool &flag);
int main(){
	int i, n, m,ans;
	bool flag;
	memset(dp, 0, sizeof(dp));
	dp[0][0] =1;
	for (i = 1; i <10; i++){
		dp[i][0] = dp[i - 1][0] * 9 - dp[i - 1][1];
		dp[i][1] = dp[i - 1][0];
	}
	while (1){
		scanf("%d%d", &n, &m);
		if (n==0&&m==0)
			break;
		flag = 0;
		ans = Compute(m, flag);
		flag = 0;
		ans -= Compute(n,flag);
		if (!flag)
			ans++;
		printf("%d\n", ans);
	}
	return 0;
}
int Compute(int x,bool &flag){
	int i, len, ans;
	len = ans = 0;
	while (x){
		bit[++len] = x % 10;
		x /= 10;
	}
	bit[len + 1] = 0;
	//-------------------------------------------------------
	for (i = len; i > 0; i--){
		if (flag)  //原数字出现了4或者62
			break;
		if (bit[i + 1] == 6 && bit[i] > 2) //上一位是6时本位不可选择2
			ans -= dp[i][1];
		if (bit[i] > 6)
			ans -= dp[i - 1][1];   //本位大于6时,当本位选择6 ,下一位选择2的都要减去
		if (bit[i] > 4)
			ans += (bit[i] - 1)*dp[i - 1][0];    //大于4的时候本位是不可以选择4的
		if (bit[i] <= 4)
			ans += bit[i] * dp[i - 1][0];
		if (bit[i] == 4||(bit[i + 1] ==6 && bit[i] == 2))  //判断是否出现4或者62
			flag = true;
	}
	if (!flag)    //判断原数字是否符合要求
		ans++;           
		return ans;
}


 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值