题意
给定 n ,问区间 [1, n]中,求有多少个数字满足以下条件:
1.包含字串 13
2.能被 13 整除
思路
区间DP。
dp[i][j][k] 第 i 位,对 13 取余为 j,状态为 k 的数字的个数。
其中 k = 0,末位不是0;k = 1,末位是 1;k = 2,已经包含13。
特别的,dp 保存的是第 i 位及后面的位都不受 n 限制时的个数。即那个关键的位在第 i 位之前。
记忆化搜索。
dp的三维作为前三个参数,第四个参数为一个 bool 型,表示当前位的选取是否受到 n 的限制。
题目链接
http://acm.hdu.edu.cn/showproblem.php?pid=3652
AC代码
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
int n;
int A[15]; //保存n的各个位
int dp[15][15][3]; //第i位对13取模为j且末位不是1、末位是1、已经含字串13的数量
//这里保存的是i及后面的位不受n限制的数量,即关键位在i前面
int dfs(int p, int mod, int situ, bool ceil) //位置,模,状态,是否受到限制
{
if(p == 0) return mod == 0 && situ == 2; //搜索到最后一位,返回是否合法
if(!ceil && dp[p][mod][situ] >= 0) return dp[p][mod][situ]; //记忆化搜索
int res = 0; //当前所求值
int ub = ceil ? A[p] : 9; //判断p位能选择的上界
for(int i= 0; i<= ub; i++)
{
int new_mod = (mod * 10 + i) % 13; //更新模
int new_situ = situ; //更新状态
if(situ == 0 && i == 1) new_situ = 1;
if(situ == 1 && i != 1) new_situ = 0;
if(situ == 1 && i == 3) new_situ = 2;
res += dfs(p - 1, new_mod, new_situ, ceil && i == ub); //累加值
}
if(!ceil) dp[p][mod][situ] = res; //如果当前就是p位不受限制的状态,记录答案
return res;
}
int main()
{
while(scanf("%d", &n) != EOF)
{
int len = 0;
while(n)
{
A[++ len] = n % 10;
n /= 10;
}
memset(dp, -1, sizeof dp);
printf("%d\n", dfs(len, 0, 0, true));
}
return 0;
}