HDU 3943 K-th Nya Number(数位DP)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3943

题目大意:求出区间 (P,Q] 中找到第K个满足条件的数,条件是该数包含X个4和Y个7

Sample Input
1
38 400 1 1
10
1
2
3
4
5
6
7
8
9
10
 
Sample Output
Case #1:
47
74
147
174
247
274
347
374
Nya!
Nya!

分析一:

  先预处理dp[i][j][k]表示第 i 位有 j 个4和 k 个7的数量。之后就可以通过高位开始枚举,求出区间内有多少个规定的数,如果询问大于总数,则输出“Nya!”

  之后找第k大的数:首先可以确定出位数,dp[i][x][y]表示 i 位时的满足数,那么大于dp[len-1][x][y],而小于dp[len][x][y],len表示目标位数。

  确认了位数之后,依然从高位开始。比如说高位首先是0,而dp[len-1][x][y]小于k,说明0开头的目标小于所求,继续往后找,把之前的删掉

  有点意思

代码如下:

  1 # include<iostream>
  2 # include<cstdio>
  3 # include<cstring>
  4 # define LL __int64
  5 using namespace std;
  6 
  7 LL dp[25][25][25]; //dp[i][j][k]表示i位的数,有j个4,k个7的数量
  8 LL l,r;
  9 int x,y;
 10 
 11 void init()
 12 {
 13     int i,j,k;
 14     memset(dp,0,sizeof(dp));
 15     dp[0][0][0] = 1;
 16     for(i=1; i<=21; i++)
 17         for(j=0; j<i; j++)
 18             for(k=0; k+j<i; k++)
 19             {
 20                 dp[i][j][k+1] += dp[i-1][j][k];
 21                 dp[i][j+1][k] += dp[i-1][j][k];
 22                 dp[i][j][k] += dp[i-1][j][k]*8; //在高位上加除4、7以外的8个数字
 23             }
 24 }
 25 
 26 LL get_count(LL n)  
 27 {
 28     int bit[25],len=0;
 29     while(n)
 30     {
 31         bit[++len] = n%10;
 32         n /= 10;
 33     }
 34     LL ans = 0;
 35     int cx=x, cy=y;
 36     for(int i=len; i; i--)  //从高位开始枚举
 37     {   
 38         for(int j=0; j<bit[i]; j++)
 39             if(j==4)
 40             {
 41                 if(cx)
 42                     ans += dp[i-1][cx-1][cy];
 43             }
 44             else if(j==7)
 45             {
 46                 if(cy)
 47                     ans += dp[i-1][cx][cy-1];
 48             }
 49             else
 50                 ans += dp[i-1][cx][cy];
 51         if(bit[i]==4)
 52             cx--;
 53         if(bit[i]==7)
 54             cy--;
 55         //如果高位出现的4、7的数量已经超过所求,则退出
 56         if(cx<0 || cy<0)
 57             break;
 58     }
 59     return ans;
 60 }
 61 
 62 LL solve(LL k)
 63 {
 64     int len = 1;
 65     while(1)
 66     {   
 67         //找到目标长度
 68         if(dp[len-1][x][y]<k && dp[len][x][y]>=k)
 69             break;
 70         len++;
 71     }
 72     LL ret = 0;
 73     int cx=x, cy=y;
 74     for(int i=len; i; i--)
 75     {
 76         //从高位开始枚举
 77         for(int j=0; j<10; j++)
 78         {
 79             int tx = cx,ty=cy;
 80             if(j==4)
 81             {
 82                 tx--;
 83                 if(tx<0)
 84                     continue;
 85             }
 86             if(j==7)
 87             {
 88                 ty--;
 89                 if(ty<0)
 90                     continue;
 91             }
 92             if(dp[i-1][tx][ty] >= k)
 93             {
 94                 ret = ret*10+j;
 95                 cx=tx;
 96                 cy=ty;
 97                 break;
 98             }
 99             k -= dp[i-1][tx][ty];
100         }
101     }
102     return ret;
103 }
104 
105 int main()
106 {
107     init();
108     int T,cas;
109     scanf("%d",&T);
110     for(cas=1; cas<=T; cas++)
111     {
112         scanf("%I64d%I64d%d%d",&l,&r,&x,&y);
113         LL a=get_count(r+1);
114         LL b=get_count(l+1);    //注意左开区间
115         int n;
116         scanf("%d",&n);
117         printf("Case #%d:\n",cas);
118         while(n--)
119         {
120             LL k;
121             scanf("%I64d",&k);
122             if(k > a-b)
123                 puts("Nya!");
124             else
125                 printf("%I64d\n",solve(k+b));
126         }
127     }
128     return 0;
129 }

 分析二:

  将solve() 函数改成二分写法,答案更简洁

 1 LL solve(LL k)
 2 {
 3     LL mid,ans=-1,ret,left=l,right=r;
 4     while(left<=right)
 5     {
 6         mid = (left+right)>>1;
 7         ret = get_count(mid+1);
 8         if(ret>=k)
 9         {
10             ans =mid;
11             right = mid-1;
12         }
13         else
14         left=mid + 1;
15     }
16     return ans;
17 }

 

你可能感兴趣的:(number)