其他分享
首页 > 其他分享> > 数位dp(洛谷 P4124 [CQOI2016]手机号码)

数位dp(洛谷 P4124 [CQOI2016]手机号码)

作者:互联网

题目链接
普通数位dp,不过有几点值得深思。
首先,我们每个手机号的位数固定为11位,即,不满足11位的并不是手机号不满足条件。而且如果像普通数位dp那样求该数到0之间的数量的话,还要考虑前导0的印象。(多开一维数组存前导0)不太值得。所以,我们直接求该数到1e10之间的数:

    int mx = flag?su[cur]:9;
    int bon = (cur==len-1)?1:0;
    ll ans = 0;
    for (int i = bon; i<=mx; i++)
    {
    //...........
    }

此后我们设:

dp[i][p1][p2][sg1][sg2] 为考虑前i位,最后一个数字为p2,倒数第二个数字为p1,已有三个连续数相等(sg1==1)、没有三个连续数相等(sg1 ==0)的情况。其中8、4存在的情况由sg2决定:

sg2 == 0 无4、无8
sg2 == 1 有4、无8
sg2 == 2 无4、有8
sg2 == 3 有4、有8

然后就是标准数位dp过程
下面是ac代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <string>
#include <algorithm>
#include <cstdlib>
#include <queue>
#include <map>
#include <vector>
#define ll long long
using namespace std;
int dp[20][11][11][5][5];
int su[20];
int len;
ll dfs(int cur, int pre1, int pre2, int sg1, int sg2, bool flag)
{
    //cout << cur << " " << pre1 << " " << pre2 <<" " << sg1 << " " <<sg2 << endl;
    if (cur < 0)
        return sg1 && sg2 != 3;
    if (!flag&&dp[cur][pre1][pre2][sg1][sg2] != -1) return dp[cur][pre1][pre2][sg1][sg2];
    int mx = flag?su[cur]:9;
    int bon = (cur==len-1)?1:0;
    ll ans = 0;
   // cout << bon <<" " << mx << endl;
    for (int i = bon; i<=mx; i++)
    {
        int nowsg2;
        if (i == 4)
        {
            if (sg2 == 0) nowsg2 = 1;
            else if (sg2 == 1) nowsg2 = 1;
            else if (sg2 == 2) nowsg2 = 3;
            else nowsg2 = 3;
        }
        else if (i == 8)
        {
            if (sg2 == 0) nowsg2 = 2;
            else if (sg2 == 1) nowsg2 = 3;
            else if (sg2 == 2) nowsg2 = 2;
            else nowsg2 = 3;
        }
        else nowsg2 = sg2;//找当前sg2
        int nowsg1;
        if (sg1 == 1) nowsg1 = 1;
        else
        {
            if (cur == len-1 || cur == len-2) nowsg1 = 0;
            else
            {
                if (i == pre1 && i == pre2) nowsg1 = 1;
                else nowsg1 = 0;
            }
        }//找当前sg1
        ans += dfs(cur-1, pre2, i, nowsg1, nowsg2, flag&&i==mx);//转移
    }
    if (!flag) dp[cur][pre1][pre2][sg1][sg2] = ans;
    return ans;
}
ll so(ll n)
{
    len = 0;
    while(n)
    {
        su[len++] = n%10;
        n /= 10;
    }
    if (len != 11) return 0;//注意特判,因为这里wa了一发,因为l-1可能是9999999999.
    return dfs(len-1, 0, 0, 0, 0, 1);
}
int main()
{
    ll r, l;
    scanf("%lld%lld", &l, &r);
    memset(dp, -1, sizeof(dp));
    ll ansr = so(r);
    ll ansl = so(l-1);
    printf("%lld\n", ansr - ansl);
}

标签:11,CQOI2016,洛谷,int,sg2,sg1,include,dp
来源: https://blog.csdn.net/qq_35802619/article/details/99698441