CDOJ1689 吉利数字 [按位dp]

题目地址:http://acm.uestc.edu.cn/problem.php?pid=1689&cid=167

按位dp+二分。

  先按位dp出一个数组:dp[loc][k][k1][k2];
  表示有loc位,最高位为k,低位全为0的数M,区间[0,M]内含有k1个6,k2个8的数字的个数。比如dp[10][3][2][4]表示区间[0,3000000000]内,有2个6,4个8的数的个数。

  求[0,L]内的有k1个6,k2个8的数的个数可以利用dp矩阵组合求解。

  先求出[0,L-1]内有X个6和Y个8的数的个数n1,[0,R]内有X个6和Y个8的数的个数n2,于是要求的数num是满足[0,num]内有n1+K的最小num。于是在[L,R]间二分求解。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<string>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<map>
#include<vector>
#include<queue>
#define sf scanf
#define pf printf
#define pfn printf("\n");
#define ll long long
#define INF 0x7fffffff

using namespace std;

ll L,R,x,y,q,k,ans,n1,n2,temp;
ll dp[18][10][19][19];//有loc位,最高位为k,低位全为0的数M,区间[0,M]内含有k1个6,k2个8的数字的个数。

ll length(ll n){
    ll len=0;
    if(n==0) return 0;
    while(n){
        n/=10;
        len++;
    }
    return --len;
}

void DP(){
    ll loc,i,k1,k2,t1,t2,j;
    for(loc=0; loc<18; loc++)
        for(i=0; i<=9; i++)
            for(k1=0; k1<=18; k1++)
                for(k2=0; k2<=18; k2++)
                    dp[loc][i][k1][k2]=0;
    //construct the kernel
    dp[0][0][0][0]=1;
    for(i=1; i<=9; i++){
        dp[0][i][0][0]=dp[0][i-1][0][0];
        dp[0][i][1][0]=dp[0][i-1][1][0];
        dp[0][i][0][1]=dp[0][i-1][0][1];
        if(i!=6 && i!=8)
            dp[0][i][0][0]++;
        else if(i==6)
            dp[0][i][1][0]++;
        else if(i==8)
            dp[0][i][0][1]++;
    }
    //states trans
    for(loc=1; loc<18; loc++){
        for(k1=0; k1<=loc+1; k1++) for(k2=0; k2+k1<=loc+1; k2++){
            for(i=1; i<=9; i++){
                dp[loc][i][k1][k2]=dp[loc][i-1][k1][k2];
                t1=k1; t2=k2; j=loc-1;
                if(i==7) t1--;
                if(i==9) t2--;
                while(j>=0){
                    dp[loc][i][k1][k2]+=dp[j][9][t1][t2];
                    j--;
                }
            }
        }
    }
}

ll find(ll k2,ll x,ll y,ll len){
    ll k=0,num=0;
    while(k2){
        k*=10;
        k+=k2%10;
        k2/=10;
    }
    while(len>=0){
        num+=dp[len][k%10][x][y];
        if(k%10==6) x--;
        //if(x<0) x=0;
        if(k%10==8) y--;
        //if(y<0) y=0;
        if(x<0 || y<0) break;
        k/=10;
        len--;
    }
    return num;
}

void D(ll L, ll R,ll x,ll y,ll k){
    ll mid=(L+R)/2,t,t1,t2;
    if(L==R){
        ans=L;
        return ;
    }
    t=find(mid,x,y,length(mid));
    if(t<k)
        D(mid+1,R,x,y,k);
    else
        D(L,mid,x,y,k);
}

int main()
{
    ll T,i,t=0;
    cin>>T;
    temp=1;
    for(i=1; i<=18; i++) temp*=10;
    DP();
    while(T--){
        t++;
        printf("Case #%lld:\n",t);
        sf("%lld %lld %lld %lld %lld",&L,&R,&x,&y,&q);
        if(R==temp) R--;
        if(L==temp) L--;
        if(x<=18 && y<=18){
            n1=find(L-1,x,y,length(L-1));
            n2=find(R,x,y,length(R));
        }
        for(i=0; i<q; i++){
            sf("%lld",&k);
            if(n1+k>n2 || x>18 || y>18) cout<<"That's too bad!\n";
            else{
                D(L,R,x,y,n1+k);
                cout<<ans<<endl;
            }
        }
    }
    return 0;
}
原文地址:https://www.cnblogs.com/Lattexiaoyu/p/2550878.html