zoukankan      html  css  js  c++  java
  • ZOJ 2599 Graduated Lexicographical Ordering (数位DP)

    首先要吐两行槽:看到集训队论文上有这道题,由于数位DP一律写成记忆化搜索形式的强迫症,就没去看论文上的几个函数是什么……;结果被这道题虐的脑细胞死光……,最后是用随机数据对拍AC程序然后发现BUG改掉后才过掉的,花了一整天时间……。回头看论文,发现差不多……。

    大概题意:给出一个long long范围的数N构成区间[1,N]和K(K<N),然后给出数的排名规则(参见原题),看到第一问求K的排名,稍稍考虑后觉得……这还用做?,第二问求第K大的数是什么,脑补N久之后感叹……这也能做?!  好吧,第一问是挺经典的数位统计问题,较简单,第二问,比较困难,很锻炼逆向思维。

    思路:第一问大体解题轮廓:如果求出了位数为i,数位和为j的个数,然后把K按区间划分……,差不多是这样。细节处理:K的各位和为SUM,比SUM小的都加上,等于SUM字典序小于K的也加上……于是把比K排名小的数拆成这两部分计算会比较简单,设dp[i][j]为i位小于j的个数(含前导零),dp2[i][j]为i位等于j的个数(不含前导零)……,(其实dp2[i][j]可以用dp[i][j+1]-dp[i][j]表示,为了思路清晰就分开存了……,而且记忆化搜索懒惰求dp值的话,应该会出错……),第一部分区间划分比较简单,只需要考虑别超过N的限制就OK了,所以记忆化搜索只需要设一个标志f1:划分上界不超过N。第二部分繁琐些……,而且需要考虑当前是否有前导零(不明白的仔细考虑一下),觉得至少需要三个标志:f1:前导零标志,f2:划分上界不超过K(字典序限制),f3:划分上界不超过N……。这样就差不多了……

    第二问大体解题轮廓:根据排名算数是不能按数位处理的……,所以要倒过来想:排名为K的数的SUM是多少?字典序又是如何的?  排名为K的SUM用第一问的第一个函数就能求出,枚举SUM的值判断是否大于K,大于K就停下,由于dp[i]数组的值是单调的,所以可以二分处理……。设SUM=X,那么第K个数就是SUM=X的所有数中字典序排名为(K-数位和<sum)的……,设这个排名为DX。然后这就又可以利用第一问的第二个函数去按位夹逼……,其实枚举每一位的值时也有单调性,可以二分,不过只枚举0~9,就没这必要了吧……,万一二分再写错……。细节注意:把SUM分配给每一位,SUM减到零时并不一定就是所求,可能还有后导零……。

    不知道怎么说的更清楚点了……,还是多动脑想细节吧……。

     1 #include <cstdio>
     2 #include <cstring>
     3 #include <algorithm>
     4 
     5 using namespace std;
     6 typedef long long  ll;
     7 
     8 ll dp[22][222];
     9 ll dp2[22][222];
    10 int d1[22],d2[22];
    11 ll dfs(int w,int sum,bool f1)
    12 {
    13     if(sum<=0)return 0;
    14     if(!w)return sum>0;
    15     if(!f1&&~dp[w][sum])return dp[w][sum];
    16     int e=f1?d1[w]:9;
    17     ll ans=0;
    18     for(int i=0;i<=e;++i)
    19         ans+=dfs(w-1,sum-i,f1&&(i==e));
    20     if(!f1)dp[w][sum]=ans;
    21     return ans;
    22 }
    23 
    24 ll dfs2(int w1,int w2,int sum,bool f1,bool f2,bool f3)
    25 {
    26     if(sum<0)return 0;
    27     if(!w1)return sum==0;
    28     if(!f2&&!f3&&~dp2[w1][sum])return dp2[w1][sum];
    29     int e=f3?d1[w1]:9;
    30     ll ans=0;
    31     if(f2)e=min(e,d2[w2]);
    32     for(int i=0;i<=e;++i)
    33     {
    34         if(!i&&f1)ans+=dfs2(w1-1,w2,sum,1,1,0);
    35         else ans+=dfs2(w1-1,w2+1,sum-i,0,f2&&(i==d2[w2]),f3&&(i==d1[w1]));
    36     }
    37     if(!f2&&!f3)dp2[w1][sum]=ans;
    38     return ans;
    39 }
    40 
    41 ll cal(ll n,ll m)
    42 {
    43     int c1,c2,sum;
    44     for(c1=0;n;d1[++c1]=n%10,n/=10);
    45     for(c2=0,sum=0;m;d2[++c2]=m%10,sum+=d2[c2],m/=10);
    46     d2[c2+1]=-1;
    47     for(int i=0;i<c2/2;++i)swap(d2[i+1],d2[c2-i]);
    48     return dfs(c1,sum,1)-1+dfs2(c1,1,sum,1,1,1);
    49 }
    50 ll cal2(ll n,ll k)
    51 {
    52     int c1,c2=1;
    53     for(c1=0;n;d1[++c1]=n%10,n/=10);
    54     int l=0,r=222,m=(l+r)>>1;
    55     while(r>l)
    56     {
    57         if(dfs(c1,m,1)-1>=k)r=m;
    58         else l=m+1;
    59         m=(l+r)>>1;
    60     }
    61     // the kth sum is m-1
    62     int sum=--m;
    63     ll dx=k-(dfs(c1,m,1)-1);
    64     memset(d2,-1,sizeof(d2));
    65     while(sum>0)
    66     {
    67         for(d2[c2]=min(sum,9);d2[c2]>=0;--d2[c2])
    68             if(dx>dfs2(c1,1,m,1,1,1))break;
    69         sum-=d2[c2++];
    70     }
    71     if(dfs2(c1,1,m,1,1,1)!=dx)
    72     {
    73         bool flag=false;
    74         if(c2>2&&d2[c2-1]==1&&d2[c2-2]<9)
    75         {
    76             ll t=d2[c2-1];
    77             d2[--c2-1]+=t,d2[c2]=-1;
    78             if(dfs2(c1,1,m,1,1,1)==dx)flag=true;
    79             else d2[c2-1]-=t,d2[c2++]=t;
    80         }
    81         if(!flag)
    82             do{d2[c2++]=0;}while(dfs2(c1,1,m,1,1,1)!=dx);
    83     }
    84     ll ans=0;
    85     for(int i=1;i<c2;++i)
    86         ans*=10,ans+=d2[i];
    87     return ans;
    88 }
    89 int main()
    90 {
    91     ll n,m;
    92     memset(dp,-1,sizeof(dp));
    93     memset(dp2,-1,sizeof(dp2));
    94     while(~scanf("%lld%lld",&n,&m)&&(n+m))
    95         printf("%lld %lld
    ",cal(n,m),cal2(n,m));
    96     return 0;
    97 }
    View Code
  • 相关阅读:
    C# 多线程编程及其几种方式
    多态有几种表现形式
    闭包的理解
    C# 泛型类型约束 where
    HTML扩展--HTMLTestRuner HTML测试报告
    编写web自动化测试
    unittest单元测试框架之认识unittest
    自动化测试模型
    处理HTML5视频播放、滑动解锁
    操作Cookie调用JavaScript
  • 原文地址:https://www.cnblogs.com/mcflurry/p/3686145.html
Copyright © 2011-2022 走看看