3 1 50 500
0 1 15HintFrom 1 to 500, the numbers that include the sub-sequence "49" are "49","149","249","349","449","490","491","492","493","494","495","496","497","498","499", so the answer is 15.
求1 ——n中含有49的数有多少……
表示做过的题都不想说啥
状态:f[i][0] i位的数字 没有不符合条件的数 f[i][1] i位的数字 没有不符合条件的数但最低位(第i位)是4 f[i][2] i位的数字 有不符合条件的数转移方程:(dfs的写法添数是添到后面的)f[i][0] = f[i - 1][0] * 10- f[i - 1][1]; f[i][1] = f[i - 1][0];f[i][2] = f[i - 1][2] * 10 + f[i - 1][1];边界:f[0][0] = 1; 表示如果是原来我一定会这么做得的, 然后在算小于x的符合那些奇奇怪怪的条件的数的个数的时候写循环把自己想到要死都弄不出来…… 这次终于把递归的看会了…… 具体到代码里面分析好了
#include <iostream> #include <string> #include <cstdio> #include <cstring> using namespace std; int bit[40]; long long f[40][4]; long long dp(int pos, int st, bool flag) //pos表示所在位; st为状态, st=0 没有49, st=1 前一位为4,st=2,已经出现过49,;flag表示高位是否是与原数不同的 { if (pos == 0) return st == 2; //所有位都已扩展完,如果st==2 返回1,否则返回0 if (flag && f[pos][st] != - 1) return f[pos][st]; //如果高位与原数不同且已经算过,直接返回 long long ans = 0; int x = flag ? 9 : bit[pos]; //维护当前位最多是多少 for (int i = 0; i <= x; i++) { if ((st == 2) || (st == 1 && i == 9)) //如果已经出现49 或者上一位为4,当前位为9 ans += dp(pos - 1, 2, flag || i < x); //算从当前状态扩展过去的下一位 else if (i == 4) ans += dp(pos - 1, 1, flag || i <x); //当前位是4, 将向下扩展的st改成1 else ans += dp(pos - 1, 0, flag || i < x); //st维持0 } if (flag) f[pos][st] = ans; //记忆化 return ans; } long long calc(long long x) { int len = 0; while (x) { bit[++len] = x % 10; x = x / 10; } //拆位 dp(len, 0, 0); //记忆化搜索进行计算 } int main() { int t; scanf("%d", &t); memset(f, -1, sizeof(f)); for (int i = 1; i <= t; i++) { long long n; cin >> n; cout << calc(n) << endl; //算小于n的符合条件的数 } return 0; }