B-number(入门数位DP)

A wqb-number, or B-number for short, is a non-negative integer whose decimal form contains the sub- string “13” and can be divided by 13. For example, 130 and 2613 are wqb-numbers, but 143 and 2639 are not. Your task is to calculate how many wqb-numbers from 1 to n for a given integer n.
Input
Process till EOF. In each line, there is one positive integer n(1 <= n <= 1000000000).
Output
Print each answer in a single line.
Sample Input
13
100
200
1000
Sample Output
1
1
2
2

题目大意为:给定一个数n,求从1到n中能整除13并且该数要存在连续的13的数字的个数,比如13,130,2613。
143能整除13但因为1和3不连续。
2639能整除13但是不存在连续的数字13
样例中:13 存在13所以答案为1
100 存在存在13所以答案为1
200存在13和130所以答案为2
1000存在13和130所以答案为2
一开始想到的是暴力枚举i,让其乘13并且限制其小于n,然后利用数组存储这个数所有的位,判断是否存在连续的13,但是提交后果然TLE了。

正确的方法是数位DP,思路及方法见代码注释:

#include<iostream>
#include<memory.h>
#include<string>

using namespace std;
typedef long long ll;
//就这个题而言,不开long long也是可以的 
ll num[15];
ll dp[15][15][10][2][2];
//定义五维dp数组,第i维对应表示dfs中的第i个变量,i=1,2,3,4,5
//1:当前位数  2:模13的结果  3:上一位的数字  4:是否符合同时存在13  5:第pos位是否存在上限限制 
ll dfs(int pos,int mod,int last,int all,int limit)
{
    if(pos==-1)
	{
        if(mod==0&&all==1)
			return 1;
        return 0;
    }
    //枚举完所有位后,符合要求的数:该数模13等于0即mod=0,该数同时存在1、3即all=1 
    if(dp[pos][mod][last][all][limit]!=-1)
		return dp[pos][mod][last][all][limit];
	//记忆化搜索,若这个状态的数已经枚举过直接return结果就好了
	ll ans=0;
    int x=limit?num[pos]:9;
	//x即上限对应的数字,若有limit限制则等于num[pos]的值,否则等于9
    for(int i=0;i<=x;i++)
	{
        if(last==1&&i==3)
			ans+=dfs(pos-1,(mod*10+i)%13,i,1,limit&&i==x);
        else
			ans+=dfs(pos-1,(mod*10+i)%13,i,all,limit&&i==x);
    }
    /*
	循环的解释: 
    枚举是肯定是从最高位开始枚举,而最高位的限制肯定为对应的num[max],而下一位的限制就为9 
	比如输入2613时,最高位从0开始枚举而且最高位limit的值1,limit&&i==x值为false 
	故第二位枚举的限制时x=9,直至枚举至0999在开始枚举最高位等于1,枚举至1999 
	当最高位=2时,limit&&i==x值变为true,参数的下一次传递limit改为true,此时第二位的限制就为num[pos]
	*/ 
    dp[pos][mod][last][all][limit]=ans;
    //保存这一状态的结果
    return ans;
}
int main()
{
	string str;
	while(cin>>str)
	{
		for(int i=0;i<str.length();i++)
			num[str.length()-i-1]=str[i]-'0';
		/*
		数组的转换,因为n上限不超过1e9,也可以用:
		ll n;
		cin>>n;
		int len=0;
		while(n)
		{
			num[len++]=n%10;
			n/=10;
		} 
		*/
		memset(dp,-1,sizeof(dp));
		//每次输入数据都要对dp数组初始化为-1 
		cout<<dfs(str.length()-1,0,0,0,1)<<endl;
		//1、函数中的第一位为位数str.length(),但是数组从0开始,所以要减1
		//2、第二位为模13的结果,因为需要找的答案模13等于,则只要枚举完所有位,
		//最后的mod等于0即符合要求
		//3、第三位为上一位的数字,用于判断是否有连续的13
		//4、若有连续的13,则等于1,若没有则等于0
		//5、用于是否对每一位的枚举上限进行限制
	}
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值