(p.s: 第2道数位dp的题目,不需要考虑前导0的问题,比hihocoder1033相对简单一些。。。。)
题目来源:
HihoCoder1301
题目要求:
对于一个十进制的数字,我们规定如果数字串中包含数字4或7,那么说明它是一个“好”的数字,现在将所有的“好”的数字从小到大排列,给出一个数字k,要求给出排在第k位的“好”数字。
解答:
本题的数据范围很大,通过枚举的方式找到答案的策略是不现实的,也一定会超时。比较高效的解法是对于各个数位上的数字进行动态规划求解。
·状态定义:
对于数字位数在i位以内的数数字,定义状态如下:
dp[i][0]: 最高位不是4和7的情况下,i位数以内的“好”数字的数目。
dp[i][1]: 最高位是4和7的情况下,i位数以内的“好”数字的数目。
例如:如果i = 1, 1位数中的“好”数字只有4和7,因此:dp[1][1] = 1, dp[1][0] = 0; 如果 i = 2, 那么,如果十位数是4或者7,个位数的取任何数字,该数字都是“好”的,因此dp[2][1] = 10, 而十位数字如果是其他的数字,那么个位数字就一定得是4或者7,才可以使得数字是“好”的,因此dp[2][0] = 2。
对于更平凡的情况,dp[i][0]和dp[i][1]的计算方式如下:
dp[i][0]表示最高位不是4和7的情况下的i位数以内的“好”数字个数,因此,除去第i位以后,就必须要争它的低i-1位数字是“好”的,而i-1位以内的“好”数字的个数又分为两种情况:如果i-1位中的最高位不是4或7,那么,“好”数字的个数为dp[i-1][0],这样情况有:0, 1, 2, 3, 5, 6, 8, 9,共8中情况;如果i-1位中的最高位是4或7,那么,“好”数字的个数为dp[i-1][1],这样的情况有4,7两种。因此:
dp[i][0] = dp[i-1][0] * 8 + dp[i-1][1] * 2;
dp[i][1]表示最高位是4或7的情况下的i位数中的“好”数字的个数,此时由于最高位已经包含4或7,因此它的低i-1位无论去什么值,整个数字都是合法的,而i-1位数字共有10^(i-1)个,因此:
dp[i][1] = 10 ^ (i-1)
通过计算表明,对于给定的数据范围,20位的十进制数字,就可以包含所有的输入情况。因此i的最大值为20。通过反复的迭代求解,就可以将整个状态表完成。
·计算:
给出状态表后,对于给定的数字k,需要找到排在第k位的“好”数字,步骤如下:
(1) 定义sum[i]表示i位数以内的所有“好”数字的数目,显然:
s[i] = dp[i][0] * 8 + dp[i][1] * 2
(2) 遍历s[1], s[2], ... s[20], 如果对于某个数值L, s[L-1] < k, 而s[L] ≥ k,那么说明我们要找的数字是一个L位的数。然后将k减小s[L-1],接下来的工作是寻找L位数中的排位在第k位的数字。
(3) 对于某一个确定的L值,定义t[j],表示最高位取1, 2, ... j的情况下的“好”数字的数目,t[j]的计算方式如下:
(i) 显然:t[0] = dp[L][0]
(ii) 对于平凡的t[j],如果j不是4也不是7,那么:t[j] = t[j-1] + dp[L][0]; 如果j是4或7,那么:t[j] = t[j-1] + dp[L][1]。
通过以上的方法就可以计算出t[0], t[1], t[2] ... t[9]的值。
(4) 遍历t[1], t[2], t[3]...t[9],对于某个数值M,如果t[M-1] < k, 而t[M] ≥ k,那么说明当前最高位(第L位)的数值就是M,同时将k减少t[M-1],并将L减1, 接下来的工作是寻找L位数中的排在第k位的数字。
(5) 本步骤将会反复地迭代进行,本步骤分为三种情况:
(i) 如果上一轮迭代中的M值为4或者7,那么当前位置取任何数字都是可以的,因此排在第k位的数字就是k-1(由于存在数字0),因此,直接将剩余的L-1填充数值k-1即可,此时迭代结束,输出最后的答案,注意前导0的处理。
(ii) 如果上一轮的迭代中的M值不是4,也不是7,那么本步骤同样需要为数值L计算序列t[j],然后遍历t[0], t[1], ... t[9] (注意,这个时候的L位数字只是最终结果的一部分,因此最高位是可以取0的,我们需要从t[0]开始遍历),同样,当遇到某个数值M,使得:t[M-1] < k, 而t[M] ≥ k,则说明当前位的数值就是M。然后L减1,继续执行本步骤。
(iii) 如果当前的迭代中L的值已经为0,那么说明最终的答案已经产生,迭代结束。
以上是计算的步骤。
输入输出格式:
输入:输入数据包含一行一个整数k
输出:输出数据包含一行,表示对应的十进制数字串。
数据范围:
题目来源:
HihoCoder1301
题目要求:
对于一个十进制的数字,我们规定如果数字串中包含数字4或7,那么说明它是一个“好”的数字,现在将所有的“好”的数字从小到大排列,给出一个数字k,要求给出排在第k位的“好”数字。
解答:
本题的数据范围很大,通过枚举的方式找到答案的策略是不现实的,也一定会超时。比较高效的解法是对于各个数位上的数字进行动态规划求解。
·状态定义:
对于数字位数在i位以内的数数字,定义状态如下:
dp[i][0]: 最高位不是4和7的情况下,i位数以内的“好”数字的数目。
dp[i][1]: 最高位是4和7的情况下,i位数以内的“好”数字的数目。
例如:如果i = 1, 1位数中的“好”数字只有4和7,因此:dp[1][1] = 1, dp[1][0] = 0; 如果 i = 2, 那么,如果十位数是4或者7,个位数的取任何数字,该数字都是“好”的,因此dp[2][1] = 10, 而十位数字如果是其他的数字,那么个位数字就一定得是4或者7,才可以使得数字是“好”的,因此dp[2][0] = 2。
对于更平凡的情况,dp[i][0]和dp[i][1]的计算方式如下:
dp[i][0]表示最高位不是4和7的情况下的i位数以内的“好”数字个数,因此,除去第i位以后,就必须要争它的低i-1位数字是“好”的,而i-1位以内的“好”数字的个数又分为两种情况:如果i-1位中的最高位不是4或7,那么,“好”数字的个数为dp[i-1][0],这样情况有:0, 1, 2, 3, 5, 6, 8, 9,共8中情况;如果i-1位中的最高位是4或7,那么,“好”数字的个数为dp[i-1][1],这样的情况有4,7两种。因此:
dp[i][0] = dp[i-1][0] * 8 + dp[i-1][1] * 2;
dp[i][1]表示最高位是4或7的情况下的i位数中的“好”数字的个数,此时由于最高位已经包含4或7,因此它的低i-1位无论去什么值,整个数字都是合法的,而i-1位数字共有10^(i-1)个,因此:
dp[i][1] = 10 ^ (i-1)
通过计算表明,对于给定的数据范围,20位的十进制数字,就可以包含所有的输入情况。因此i的最大值为20。通过反复的迭代求解,就可以将整个状态表完成。
·计算:
给出状态表后,对于给定的数字k,需要找到排在第k位的“好”数字,步骤如下:
(1) 定义sum[i]表示i位数以内的所有“好”数字的数目,显然:
s[i] = dp[i][0] * 8 + dp[i][1] * 2
(2) 遍历s[1], s[2], ... s[20], 如果对于某个数值L, s[L-1] < k, 而s[L] ≥ k,那么说明我们要找的数字是一个L位的数。然后将k减小s[L-1],接下来的工作是寻找L位数中的排位在第k位的数字。
(3) 对于某一个确定的L值,定义t[j],表示最高位取1, 2, ... j的情况下的“好”数字的数目,t[j]的计算方式如下:
(i) 显然:t[0] = dp[L][0]
(ii) 对于平凡的t[j],如果j不是4也不是7,那么:t[j] = t[j-1] + dp[L][0]; 如果j是4或7,那么:t[j] = t[j-1] + dp[L][1]。
通过以上的方法就可以计算出t[0], t[1], t[2] ... t[9]的值。
(4) 遍历t[1], t[2], t[3]...t[9],对于某个数值M,如果t[M-1] < k, 而t[M] ≥ k,那么说明当前最高位(第L位)的数值就是M,同时将k减少t[M-1],并将L减1, 接下来的工作是寻找L位数中的排在第k位的数字。
(5) 本步骤将会反复地迭代进行,本步骤分为三种情况:
(i) 如果上一轮迭代中的M值为4或者7,那么当前位置取任何数字都是可以的,因此排在第k位的数字就是k-1(由于存在数字0),因此,直接将剩余的L-1填充数值k-1即可,此时迭代结束,输出最后的答案,注意前导0的处理。
(ii) 如果上一轮的迭代中的M值不是4,也不是7,那么本步骤同样需要为数值L计算序列t[j],然后遍历t[0], t[1], ... t[9] (注意,这个时候的L位数字只是最终结果的一部分,因此最高位是可以取0的,我们需要从t[0]开始遍历),同样,当遇到某个数值M,使得:t[M-1] < k, 而t[M] ≥ k,则说明当前位的数值就是M。然后L减1,继续执行本步骤。
(iii) 如果当前的迭代中L的值已经为0,那么说明最终的答案已经产生,迭代结束。
以上是计算的步骤。
输入输出格式:
输入:输入数据包含一行一个整数k
输出:输出数据包含一行,表示对应的十进制数字串。
数据范围:
0 ≤ k ≤ 1,000,000,000,000,000,000 (10^18)
程序代码:
/****************************************************/
/* File : HihoCoder1301 */
/* Author : Zhang Yufei */
/* Date : 2016-05-09 */
/* Description : HihoCoder ACM program. (submit:g++)*/
/****************************************************/
#include<stdio.h>
#include<math.h>
/*
* The main program.
*/
int main(void) {
unsigned long long dp[21][2];
unsigned long long weight[20];
weight[0] = 1;
for(int i = 1; i < 20; i++) {
weight[i] = 10 * weight[i - 1];
}
dp[1][0] = 0;
dp[1][1] = 1;
dp[2][0] = dp[1][0] * 7 + 2 * weight[0];
for(int i = 2; i <= 20; i++) {
dp[i][1] = weight[i - 1];
dp[i + 1][0] = dp[i][1] * 2 + dp[i][0] * 8;
}
unsigned long long k;
scanf("%llu", &k);
int i;
int tag = 0;
for(i = 1; i <= 20; i++) {
if(dp[i][0] >= k) {
break;
}
}
while(k > 0) {
i--;
if(i == 0) {
break;
}
if(tag == 0) {
unsigned long long s = 0;
int j;
for(j = 0; j < 10; j++) {
int index;
if(j != 4 && j != 7) {
index = 0;
} else {
index = 1;
}
if(s + dp[i][index] >= k) {
printf("%d", j);
k -= s;
if(j == 4 || j == 7) {
tag = 1;
}
break;
} else {
s += dp[i][index];
}
}
} else {
k--;
while(i > 0) {
printf("%d", k / weight[i - 1]);
k %= (int) weight[i - 1];
i--;
}
break;
}
}
printf("\n");
return 0;
}