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 }
把每一件简单的事情做好,就是不简单;把每一件平凡的事情做好,就是不平凡!相信自己,创造奇迹~~