B数计数【数位DP】

>Link

ybtojB数计数


>Description

求1到n当中有多少个数满足:十进制下既包含子串"13",又能被13整除
其中 1 ≤ n ≤ 1 0 9 1≤n≤10^9 1n109


>解题思路

数位DP的模板题

#==
一直RE调试了超级久,后来搜系统信息“Killed: Floating point exception”,看到有人说是除0的情况,但是我的码并没有除0的情况啊😅又调了好久,无奈之下把原来的方法改成不用除法的方法,然后就A了😅

f i , j , k f_{i,j,k} fi,j,k表示前面填了 i i i位数,当前的余数为 j j j,包含"13"的状态为 k k k
k k k的含义:

  1. k = = 0 k==0 k==0:当前不包含"13"
  2. k = = 1 k==1 k==1:当前最后一位为"1"
  3. k = = 2 k==2 k==2:当前已包含"13"

我们DP,不断在后面添数,就可以算出来了。状态如何转移挺好推的,需要分类讨论,要注意下当前为1和3的情况

但是范围规定了1到n
设n有m位
位数小于m位的,显然每位数的位置可以随便填
位数等于m位的,可以发现,如果当前位填的数比 n n n对应位上的数小,那么后面位上的数就可以随便填
我们可以直接枚举 i i i,表示 i i i之前的位都和 n n n对应,让第 i i i位填一个较小的数,后面的位置进行乱填,维护一下就行了(数据范围很小)


>代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

int n, m, t, p, a[20], f[20][20][5], ff[20][20][5], ans, pmod;

void solve (int S, int T, int E)
{
	memset (ff, 0, sizeof (ff));
	for (int j = 0; j < 13; j++)
	  for (int k = 0; k < 3; k++)
	    ff[S - 1][j][k] = f[S - 1][j][k];
	for (int i = S; i <= T; i++)
	{
		for (int j = 0; j < 13; j++)
		  for (int k = 0; k < 10; k++)
		  {
			int p = (j * 10 % 13 + k) % 13;
			if (k == 1)
			  ff[i][p][1] += ff[i - 1][j][0] + ff[i - 1][j][1];
			else ff[i][p][0] += ff[i - 1][j][0];
			if (k != 1 && k != 3) ff[i][p][0] += ff[i - 1][j][1];
			if (k == 3) ff[i][p][2] += ff[i - 1][j][1];
			ff[i][p][2] += ff[i - 1][j][2];
		  }
		if (E) ans += ff[i][0][2];
	}
	if (!E) ans += ff[T][0][2];
}

int main()
{
	while (scanf ("%d", &n) != EOF)
	{
		ans = m = 0;
		memset (f, 0, sizeof (f));
		memset (a, 0, sizeof (a));
		t = 1;
		while (n / t > 0) t *= 10, m++;
		//for (t = 1; n / t != 0; t *= 10) m++;
		//t /= 10;
		//for (int i = 1; i <= m; i++)
		//  a[i] = n / t % 10, t /= 10;
		for (int i = 1; i <= m; i++)
		  a[m - i + 1] = n % 10, n /= 10; //a记录n第几位上填的是什么数
		for (int i = 2; i < 10; i++)
		  f[1][i][0] = 1;
		f[1][1][1] = 1;
		solve (2, m - 1, 1);
		
		memset (f, 0, sizeof (f));
		pmod = 0; //pmod记录当前枚举到的位置,前面的余数是多少(对于n而言)
		for (int i = 1; i <= m; i++)
		{
			if (i == 1)
			{
				for (int k = 1; k < a[i]; k++)
				{
					if (k == 1) f[i][k][1] = 1;
					else f[i][k][0] = 1;
					solve (i + 1, m, 0);
					if (k == 1) f[i][k][1] = 0;
					else f[i][k][0] = 0;
				}
				if (a[i] == 1) f[i][a[i]][1] = 1;
				else f[i][a[i]][0] = 1;
				pmod = a[i];
				continue;
			}
			for (int k = 0; k < a[i]; k++)
			{
				p = (pmod * 10 % 13 + k) % 13;
				if (k == 1)
				  f[i][p][1] += f[i - 1][pmod][0] + f[i - 1][pmod][1];
				else f[i][p][0] += f[i - 1][pmod][0];
				if (k != 1 && k != 3) f[i][p][0] += f[i - 1][pmod][1];
				if (k == 3) f[i][p][2] += f[i - 1][pmod][1];
				f[i][p][2] += f[i - 1][pmod][2];
				
				solve (i + 1, m, 0);
				
				if (k == 1)
				  f[i][p][1] -= f[i - 1][pmod][0] + f[i - 1][pmod][1];
				else f[i][p][0] -= f[i - 1][pmod][0];
				if (k != 1 && k != 3) f[i][p][0] -= f[i - 1][pmod][1];
				if (k == 3) f[i][p][2] -= f[i - 1][pmod][1];
				f[i][p][2] -= f[i - 1][pmod][2];
			}
			p = (pmod * 10 % 13 + a[i]) % 13;
			if (a[i] == 1)
			  f[i][p][1] += f[i - 1][pmod][0] + f[i - 1][pmod][1];
			else f[i][p][0] += f[i - 1][pmod][0];
			if (a[i] != 1 && a[i] != 3) f[i][p][0] += f[i - 1][pmod][1];
			if (a[i] == 3) f[i][p][2] += f[i - 1][pmod][1];
			f[i][p][2] += f[i - 1][pmod][2];
			pmod = p;
		}
		ans += f[m][0][2];
		printf ("%d\n", ans);
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值