http://acm.hdu.edu.cn/showproblem.php?pid=2089
dp[i][j]表示长度为i的数字中,开头的数字是j的时候,有多少种合法的情况。
预处理挺好理解,关键是那个统计,比较难。
比如我统计的是数字: 456
那么,从高到低枚举。先枚举第一位,4、再枚举5、....
那么,< 4 的,长度是3位的合法数字,都应该算做贡献。就是ans += dp[3][0...3]
dp[3][1]好理解,就是100、110、....199那些。
dp[3][2]那些同理,不处理到dp[3][4]也好理解,因为dp[3][4]包含了499那些,就是超越范围了。
然后,dp[3][0]是什么呢?其实就是0xx,其中,xx就是00---99
所以也就是所有的1位数和2位数的合法情况。(这个回归下dp预处理就知道了)
而后来的枚举下一位的5,dp[2][0...4]是同理的,但是其是和第一位的4结合的,也就是dp[2][0]包括了所有1位数的合法情况,然后组合成40x。
所以后面的枚举其实这是为了和上一位组合成合法情况。所以当其中某些数字破坏了条件,例如出现了4,或者已经出现了62,就要提前break
还有需要注意的是处理不到n这个数字的,因为都是枚举每一位的-1那个大小,所以只需要把他们 + 1即可。
#include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #include <algorithm> #include <assert.h> #define IOS ios::sync_with_stdio(false) using namespace std; #define inf (0x3f3f3f3f) typedef long long int LL; #include <iostream> #include <sstream> #include <vector> #include <set> #include <map> #include <queue> #include <string> #include <bitset> int dp[10][20]; void init() { dp[0][0] = 1; for (int i = 1; i <= 7; ++i) { for (int j = 0; j <= 9; ++j) { if (j == 4) continue; for (int k = 0; k <= 9; ++k) { if (k == 4 || j == 6 && k == 2) continue; dp[i][j] += dp[i - 1][k]; } } } } char str[222]; int calc(int val) { int lenstr = 0; while (val / 10 > 0) { str[++lenstr] = val % 10 + '0'; val /= 10; } str[++lenstr] = val + '0'; str[lenstr + 1] = '