首页 > 其他 > 详细

$Poj3208$ 启示录 数位统计$DP$

时间:2019-07-28 00:24:38      阅读:112      评论:0      收藏:0      [点我收藏+]

Poj  AcWing

 

Description

技术分享图片

 

技术分享图片

 

 

Sol 

这题长得就比较像数位$DP$叭.

所以先用$DP$进行预处理,再基于拼凑思想,通过"试填法"求出最终的答案.

设$F[i][3]$表示由$i$位数字构成的魔鬼数有多少个,$F[i][j](0<=j<=2)$表示由$i$位数字组成的,开头有$j$个$6$的非魔鬼数有多少个.注意,在计算$F[i][j]$时允许前导$0$的存在

$F[i][0]=9*(F[i-1][0]+F[i-1][1]+F[i-1][2])$

$F[i][1]=F[i-1][0]$

$F[i][2]=F[i-1][1]$

$F[i][3]=F[i-1][2]+10*F[i-1][3]$

预处理完成之后,我们先通过$F[i][3]$确定第$X$小魔鬼数的位数,然后从左到右进行试填,试填的数从小到大枚举

 

Code

技术分享图片
#include<iostream>
#include<cstdio>
#include<cmath>
#define Rg register
#define il inline
#define db double
#define ll long long
#define go(i,a,b) for(Rg int i=a;i<=b;i++)
#define yes(i,a,b) for(Rg int i=a;i>=b;i--)
using namespace std;
il int read()
{
    int x=0,y=1;char c=getchar();
    while(c<0||c>9){if(c==-)y=-1;c=getchar();}
    while(c>=0&&c<=9){x=(x<<3)+(x<<1)+c-0;c=getchar();}
    return x*y;
}
int T,n,d,k;
ll f[21][4];
il void init()
{
    f[0][0]=1;
    go(i,1,20)
    {
        f[i][0]=9*(f[i-1][0]+f[i-1][1]+f[i-1][2]);
        f[i][1]=f[i-1][0];
        f[i][2]=f[i-1][1];
        f[i][3]=f[i-1][2]+10*f[i-1][3];
    }
}
int main()
{
    init();T=read();
    while(T--)
    {   
        n=read();d=3;k=0;
        while(f[d][3]<n)d++;
        yes(i,d,1)
        {
            go(j,0,9)
            {
                //求若第i位为j,那么剩下i-1位有多少种填法能使这个数成为魔鬼数
                ll ct=f[i-1][3];
                if(j==6 || k>=3)
                    go(t,max(0,3-k-(j==6)),2)ct+=f[i-1][t];
                if(ct<n)n-=ct;//应该填一个更大的数
                else //就填j 
                {
                    if(j==6)k++;else if(k<3)k=0;
                    printf("%d",j);break;
                }
            }
        }
        printf("\n");
    }
    return 0;
}
View Code

 

 

 

$Poj3208$ 启示录 数位统计$DP$

原文:https://www.cnblogs.com/forward777/p/11255689.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!