题目:http://acm.hdu.edu.cn/showproblem.php?pid=3555
题意:
给出一个正整数N,求出1~N中含有数字“49”的数的个数
思路:
采用数位dp的状态转移方程法解
具体如下:
dp[len][state]; dp数组的第一位代表数字的位数,第二位代表状态
状态设定:
dp[i][0] : i 位数字中不含数字49的数的个数
dp[i][1] : i 位数字中不含数字49,但高位是9的数的个数
dp[i][0] : i 位数字中含有数字49的数的个数
状态转移方程:
dp[i][0] = dp[i-1][0] * bit[i] - dp[i-1][1];长度为 i 不含49的数的个数 = 长度为 i-1 中不含49的数的个数*当前数字(因为这个位置可以填0~bit[i]-1),然而,其中包含了bit[i]为4且长度为 i-1 的高位为9,从而组成49····的情况,所以,减去长度为 i-1 的高位为9的数的个数,dp[i-1][1] * 1(这个1代表的是bit[i]为4的情况。
dp[i][1] = dp[i-1][0];长度为 i 的高位为9但不含49的数的个数 = 长度为 i-1 中不含49的数的个数(因为只需在此基础上加上9即可构成 i 位最高位为9的情况)。
dp[i][2] = dp[i-1][2] * bit[i] + dp[i-1][1];长度为 i 的含有数字49的数的个数 = 长度为 i-1 的个数*当前数字,加上长度为 i-1 的高位为9的数字的个数(加个4就成了i位含49的数了)。
代码如下:
#include<iostream> using namespace std; int n, bit[20]; unsigned long long dp[20][3], num, ans; void init() { dp[0][0] = 1; for (int i = 1; i < 20; ++i) { dp[i][0] = dp[i - 1][0] * 10 - dp[i - 1][1]; dp[i][1] = dp[i - 1][0]; dp[i][2] = dp[i - 1][2] * 10 + dp[i - 1][1]; } } long long solve() { int len = 0, last = 0; bool flag = false; num++, ans = 0; while (num) { bit[++len] = num % 10; num /= 10; } for (int i = len; i > 0; --i) { ans += dp[i - 1][2] * bit[i]; if (flag)ans += dp[i - 1][0] * bit[i]; if (!flag&&bit[i] > 4)ans += dp[i - 1][1]; if (last == 4 && bit[i] == 9)flag = true; last = bit[i]; } return ans; } int main() { cin >> n; init(); while (n--) { cin >> num; cout << solve() << endl; } return 0; }
num++的原因是,该方式求的是开区间,而题目为闭区间,所以要+1
感谢您的阅读,生活愉快~