HDU 3652 B-number (数位DP)
题意
要求区间[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\n", cal(n)); } return 0; } [/cpp]举杯独醉,饮罢飞雪,茫然又一年岁。 ------AbandonZHANG