一道数位DP题。
题目
大意是给定一个数N,计算从1至N的数字中有多少个包含“49”;
例如500,从1到500包含"49"的数有:
"49","149","249","349","449","490","491","492","493","494","495","496","497","498","499",共15个,所以应该输出15。
代码:
#include <iostream> long long dp[22][3]; long long input; int data[21]; //dp[i][0]长度为i且含有49;注意049也算是长度为3包含49的数的个数 //dp[i][1]长度为i不含49,但高位是9的数的个数 //dP[i][2]长度为i不含49的数的个数 void init() { dp[0][0] = 0; dp[0][1] = 0; dp[0][2] = 1; for (int i = 1; i <= 21; ++i) { dp[i][0] = dp[i - 1][0] * 10 + dp[i-1][1]; dp[i][1] = dp[i - 1][2]; dp[i][2] = dp[i - 1][2] * 10 - dp[i-1][1]; } } long long howmany_49(long long x) { long long numOf_49 = 0; int len=0; while (x != 0) { ++len; data[len] = x % 10; x = x / 10; } data[0] = 0;//这里其实可以不管 data[len + 1] = 0;//这里不能取4就行 bool appear49 = false;//标志x里面是否出现"49" for (int i = len; i >= 1; --i) { numOf_49 += dp[i - 1][0] * data[i]; if (appear49 == false) { if (data[i] > 4) numOf_49 += dp[i - 1][1]; //if (data[i + 1] == 4 && data[i] > 9)//这里其实没有必要 // numOf_49 += dp[i][1]; } else { numOf_49 += dp[i-1][2] * data[i]; } if (data[i + 1] == 4 && data[i] == 9) appear49 = true; } if (appear49) numOf_49++; return numOf_49; } int main() { init(); int numOfInput; std::cin >> numOfInput; std::cout << sizeof(long) << " " << sizeof(long long); while (numOfInput--) { std::cin >> input; std::cout << howmany_49(input) << std::endl; } return 0; }计算过程是这样的,先初始化dp,把长度为1~21的数字中含有49,高位是9和不含49的数字的个数统计出来。
howmany_49()计算过程是这样的:以560为例,首先i=3,"指向"5,
numOf_49 += dp[i - 1][0] * data[i];就得到049 149 249 349 449 ,然后进入if,因为5>4,所以得到490,491,492,493,494,495,496,497,498,499。接着的if把它注释掉了,因为不可能出现>9的情况,但是,如果求含48的个数时,这个if就需要,if (data[i + 1] == 4 && data[i] > 8)。如果appear49为true,说明x的前面出现了49,那么低位就不含49的所有数都符合,这个例子560没有出现49。然后--i,i指向6,1位含49的数0个,又6〉4,1位里高位是9的只有9,得到49,加上前面的5,得到549。这里之前一直认为是得到49,但是后来想明白了,当到6这里是,前面的5就固定。然后--i到0,结束。
所以,得到:049,149,249,349,449,490,491,492,493,494,495,496,497,498,499,549,共16个。