HDU - 3555 bomb(数位DP) HQG_AC的博客

http://acm.hdu.edu.cn/showproblem.php?pid=3555
https://vjudge.net/problem/HDU-3555


题意就是求在1~n内含有49的数的个数


dp[i][j][(k)0/1]表示长度为i,首字母为j,1表示存在49的情况,0表示不存在49的情况 
1.先把dp数组搞定
  1°i=1,k=0 ,dp=1 ;
  2°
   for 枚举位数
   for 枚举前一个数
   for 枚举后一个数
     1)j=4 && k=9 只有dp[i][j][1]了,这是+=dp[i-1][k][0/1](都可以,后面的情况无所谓)
     2) else 分别加
2.读入,存数组,置换
3.输答案
  1°长度相同,枚举相同的前缀的长度(从0~s-1(s表示长度,下同))
     1)当前缀为0时,第一个数只能取1,否则程度不相同,不符合,
       ans+=dp[s-i][j][1]
     2)当前面已经拥有49出现时,后面无论去什么都没有问题,+dp[s-i][j][0/1]
       注意:这里有个坑,必须在i=1的情况就判断好当前序列含不含有1,不然在2时再枚举会出奇异错误
     3)普通,+dp[s-i][j][1]
  2°长度不同,枚举长度,再枚举首位.


见代码:


#include <bits/stdc++.h>
using namespace std ;
long long n ;
int b[31];
long long dp[41][41][2]; //dp[i][j][0/1]表示长度为i,首字母为j,1表示存在49的情况,0表示不存在49的情况 
int main(){
	int T ;
	for (int i=0;i<10;i++) dp[1][i][0]=1;
	for (int i=2;i<=30;i++){
		for (int j=0;j<=9;j++)
		for (int k=0;k<=9;k++){
			if (j==4 && k==9) { //当前元素含有49 
				dp[i][j][1]+=(dp[i-1][k][1]+dp[i-1][k][0]) ; 
			}
			else {
				dp[i][j][1]+=dp[i-1][k][1] ;
				dp[i][j][0]+=dp[i-1][k][0] ;
			}
		} 
	}
	scanf("%d",&T) ;
	for (int e=1;e<=T;e++){
		scanf("%lld",&n) ;
		memset(b,0,sizeof(b)) ;
		int s=0 ;
		while(n>0) //b数组表示数字,用字符存 
		{
			b[++s]=n%10 ;
			n/=10 ;
		}
		for(int i=1;i<=s/2;i++) //翻转 
		{ 
			int t=b[i] ;
			b[i]=b[s-i+1] ;
			b[s-i+1]=t; 
		} 
		long long ans=0 ;int flag=0 ; //flag表示 前面是否已经有49出现 
		for (int i=0;i<=s;i++) //长度相同,可以无前缀,前缀长度为i 
		{ 
			if (i==0)  //连前缀都没有 
			{
				for (int j=1;j<b[i+1];j++) ans+=dp[s-i][j][1] ;
				//前缀之后(前段没有49,有的是2段)这段含49的情况 
			}	
			else if (flag) //前几段有49了,那后面有没有都没有关系 
			{
				for (int j=0;j<b[i+1];j++) //这里错了,找了一个小时。因为当前面为49时,i是2。
				//这是后就应该符合flag=1这个条件了,所以应该是小于当前b[i+1] ,
				//相应的下面flag的判定条件也要变化 
				{
					ans+=(dp[s-i][j][1]+dp[s-i][j][0]) ; //都相加 
				}
			}
			else //普通情况 
			{
				for (int j=0;j<b[i+1];j++) ans+=dp[s-i][j][1] ;
			}
			if (i>=1 && b[i]==4 && b[i+1]==9) flag=1 ;	//相应的这里也要改掉 
		}
		if (flag) ans++ ;//该原数符合要求 
		for (int i=2;i<s;i++) //长度不相同,从2~s枚举(1不可能,s已枚举) 
		{
			for (int k=1;k<=9;k++) //枚举首位元素 
			ans+=dp[i][k][1] ;
		}
		printf("%lld\n",ans) ; 
	}
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值