Bomb HDU 3555 dp状态转移

题目: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

感谢您的阅读,生活愉快~

原文地址:https://www.cnblogs.com/lv-anchoret/p/8467126.html