题意
要求区间[1,n]范围内有多少数包含13且被13整除。
思路
包含13、且被13整除。在状态转移时,既要保存上一位的状态,又要保存之前处理的位数与13的模。
pos表示处理到当前位;mod表示之前处理的数与13的模;flag = true表示前几位出现过连续的13,更新时只需判断以前是否出现过以及当前位和上一位是否组成13即可。
当然
笔良文昌牛牛用了另一种flag状态的设计:flag = 0 表示没有1 3;
flag = 1 表示上一位是1;flag = 2 表示上两位分别是13,然后状态更新时分别根据这几种状态更新。
这种设计有个好处是可扩展性强,比如可以扩展到不止两位数的判断(出现123之类的),当然那样也许状态更新时也更加繁琐;而用我的方法需要dp方程添加一维(prepre之类的)。各有优缺点,具体问题具体对待吧~~
学会数位DP状态的灵活设计!~
代码
[cpp]
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <string>
#include <cstring>
#include <vector>
#include <set>
#include <stack>
#include <queue>
#define MID(x,y) ((x+y)/2)
#define MEM(a,b) memset(a,b,sizeof(a))
#define REP(i, begin, m) for (int i = begin; i < begin+m; i ++)
using namespace std;
typedef long long LL;
typedef vector <int> VI;
typedef set <int> SETI;
typedef queue <int> QI;
typedef stack <int> SI;
const int oo = 0x7fffffff;
VI num;
int dp[15][15][15][2];
int dfs(int pos, int pre, int mod, bool flag, int limit){
if (pos == -1) return !mod && flag;
if (!limit && ~dp[pos][pre][mod][flag]) return dp[pos][pre][mod][flag];
int end = limit?num[pos]:9;
int res = 0;
for (int i = 0; i <= end; ++ i){
res += dfs(pos-1, i, (mod*10+i)%13, flag || (i == 3 && pre == 1), limit && (i == end));
}
if (!limit) dp[pos][pre][mod][flag] = res;
return res;
}
int cal(int x){
num.clear();
while(x){
num.push_back(x%10);
x /= 10;
}
MEM(dp, -1);
return dfs(num.size()-1, 0, 0, 0, 1);
}
int main(){
//freopen("test.in", "r", stdin);
//freopen("test.out", "w", stdout);
int n;
while(~scanf("%d", &n)){
printf("%d
", cal(n));
}
return 0;
}
[/cpp]