HDU - 3555 Bomb (数位dp)

题目链接:https://cn.vjudge.net/contest/163023#problem/D
题目大意:要求1~n的范围内含有49的数字的个数
题目分析:做的数位dp的第二道题,有三种方法。
第一种(自己想到的):
求出1~n范围内不含49的个数,然后用总数减去不含49的个数再加一(多减了一个0)
代码如下:

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
int data[30];
ll dp[30][2];

ll dfs(int pos, int pre, bool limit)
{
    if(pos == -1) return 1;
    if(!limit && dp[pos][pre == 4])
    {
        return dp[pos][pre == 4];
    }
    int up = limit ? data[pos] : 9;
    ll ans = 0;
    for(int i = 0; i <= up; i++)
    {
        if(i == 9 && pre == 4) continue;
        ans += dfs(pos - 1, i, limit && i == data[pos]);
    }
    if(!limit) return dp[pos][pre == 4] = ans;
    return ans;
}

ll solve(ll n)
{
    memset(dp, 0, sizeof(dp));
    int pos = 0;
    while(n)
    {
        data[pos++] = n % 10;
        n /= 10;
    }
    ll ans = dfs(pos - 1, -1, true);
    return ans;
}

int main()
{
    ll n;
    int t;
    scanf("%d", &t);
    while(t--)
    {
        scanf("%lld", &n);
        printf("%lld\n", n - solve(n) + 1);
    }
    return 0;
}

第二种方法:
假设n是123456, 当dfs到049_ _ _时,很显然,后三位可放置任意数,共有10 * 10 * 10种放置方法,这种是!limit的情况,当n是49123时,我们跑到49
_ _ _时,后边就只有123 + 1种方法。所以只要标记好limit,预处理出没有限制的情况,就可以轻松算出结果。

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
ll dp[30][2], kind[30], n;
int data[30];

ll dfs(int pos, int pre, bool limit)
{
    if(pos == -1) return 0;
    if(!limit && dp[pos][pre == 4]) return dp[pos][pre == 4];
    int up = limit ? data[pos] : 9;
    ll ans = 0;

    for(int i = 0; i <= up; i++)
    {
        if(pre == 4 && i == 9)
        {
            ans = ans + (limit ? ((n % kind[pos]) + 1) : kind[pos]);
        }
        else ans += dfs(pos - 1, i, limit && i == data[pos]);
    }
    if(!limit)
    return dp[pos][pre == 4] = ans;
    return ans;
}

ll solve(ll k)
{
    int pos = 0;
    memset(dp, 0, sizeof(dp));
    while(k)
    {
        data[pos++] = k % 10;
        k /= 10;
    }

    return dfs(pos - 1, -1, true);
}

int main()
{
    int t, i;
    kind[0] = 1;
    for(i = 1; i <= 18; i++)
        kind[i] = kind[i - 1] * 10;

    scanf("%d", &t);
    while(t--)
    {
        scanf("%lld", &n);
        printf("%lld\n", solve(n));
    }
}

第三种方法:
就是传统的数位dp,当时实在是太差了,这都想不出来。。

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
ll dp[30][30][2], n;
int data[30];

ll dfs(int pos, int pre, bool sta, bool limit)
{
     if(pos == -1 && sta) return 1;
     if(pos == -1 && !sta) return 0;

     if(!limit && dp[pos][pre == 4][sta]) return dp[pos][pre == 4][sta];
     int up = limit ? data[pos] : 9;
     ll ans = 0;

     for(int i = 0; i <= up; i++)
     {
         if(pre == 4 && i == 9) ans += dfs(pos - 1, i, true, limit && i == data[pos]);
         else ans += dfs(pos - 1, i, sta/*注意这里是sta,而不是false, 之前出过错。*/, limit && i == data[pos]);
     }

     if(!limit) return dp[pos][pre][sta] = ans;
     return ans;
}

ll solve(ll k)
{
    int pos = 0;
    memset(dp, 0, sizeof(dp));
    while(k)
    {
        data[pos++] = k % 10;
        k /= 10;
    }
    return dfs(pos - 1, -1, false, true);
}

int main()
{
    int t, i;
    scanf("%d", &t);
    while(t--)
    {
        scanf("%lld", &n);
        printf("%lld\n", solve(n));
    }
}

猜你喜欢

转载自blog.csdn.net/deerly_/article/details/79516097