• 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;
    }
  • 相关阅读:
    VS密钥
    继承中delelte对象子类析构函数不被执行
    [LeetCode] Merge k Sorted Lists
    [LeetCode] Spiral Matrix II
    [LeetCode] Multiply Strings
    [LeetCode] Valid Number
    [LeetCode] Search Insert Position
    [LeetCode] Spiral Matrix
    [LeetCode] Valid Parentheses
    [LeetCode] Rotate List
  • 原文地址:https://www.cnblogs.com/Lattexiaoyu/p/2550878.html
Copyright © 2020-2023  润新知