HDU 3652 B-number

传送门

数位dp。
有一种数,能被“13”整除而且数位里包含“13”,求[1,n]中这种数的个数。
for short是简称的意思。

之前求过数位里不包含“XX”的,中途判断就可以了。但如果求的是包含的个数,要么取反、要么只能最后判断。
以下几点。

  1. 这道题要用三维状态,两个条件各用一维。这两个条件都是运行到最后一位(反正大概是最后)才能判断出来。
  2. 对包含“13”的判断,设计三个状态,分别表示:
    (0)之前还没出现过“13”且前一位不是“1”;
    (1)之前还没出现过“13”且前一位是“1”;
    (2)之前已出现过“13”。
  3. 对被“13”整除的判断,将 ( ∑ n = i + 1 p o s − 1 a n ⋅ 1 0 n )   m o d   13 (\sum_{n=i+1}^{pos-1}{a_{n}\cdot10^{n}})\bmod13 (n=i+1pos1an10n)mod13作为状态值。
    其实是用了一个公式: ( a + b )   m o d   c = [ ( a   m o d   c ) + ( b   m o d   c ) ]   m o d   c (a+b)\bmod c = \Big[(a\bmod c)+(b\bmod c)\Big]\bmod c (a+b)modc=[(amodc)+(bmodc)]modc
    最后判断是不是0就完事了。
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;

int n;
int dp[10][3][13];                // 牛批,没看题解一次写对
int num[10];

int dfs(int pos, int status, int remainder, bool limit)
{
     
	if (pos == -1) return ((status == 2) && (remainder == 0)) ? 1 : 0;   // 最后才能判断
	if ((!limit) && (dp[pos][status][remainder] != -1)) return dp[pos][status][remainder];

	int cnt = 0;
	int up = (limit ? num[pos] : 9);
	for (int i = 0; i <= up; i++)
	{
     
		int new_status;
		if ((status == 2) || ((status == 1) && (i == 3))) new_status = 2;
		else if (i == 1) new_status = 1;
		else new_status = 0;

		cnt += dfs(pos - 1, new_status, (remainder + (i * (int)pow(10, pos))) % 13, limit && (i == up));
	}
	if (!limit) dp[pos][status][remainder] = cnt;
	return cnt;
}

int solve(int x)
{
     
	int pos = 0;
	for (; x;)
	{
     
		num[pos++] = x % 10;
		x /= 10;
	}
	return dfs(pos - 1, 0, 0, true);
}

int main()
{
     
	memset(dp, -1, sizeof dp);
	for (; ~scanf("%d", &n);)
		printf("%d\n", solve(n));

	return 0;
}

你可能感兴趣的:(动态规划,HDOJ,动态规划,-,数位dp)