只有 2 和 5 作为其数字的特殊数字
Special Numbers having only 2 and 5 as its digit
特殊数字是一个仅由2和5作为其数字的数字,例如-2,5,22,25,52,555等。有一个函数 f(k(,它返回大于或等于 k 的最小数字,是一个特殊数字。
我的问题是,如果有两个数字L和R,我们需要找到f(k(的总和,其中L<=k<=R。我在 c++ 中解决它的方法如下:
#include <bits/stdc++.h>
using namespace std;
long long int power(long long int a, long long int b)
{
long long int prod = 1;
for (long long int i = 1; i <= b; i++)
prod *= a;
return prod;
}
long long int f(long long int k)
{
long long int nod = 0;
long long int temp = 0, carry = 0;
while (k)
{
if (k == 1)
{
temp = 0;
for (int i = nod; i >= 0; i--)
temp = temp * 10 + 2;
return temp;
}
if (((k % 10) + carry) <= 2)
{
temp = 2 * power(10, nod) + temp;
carry = 0;
}
if (((k % 10) + carry) > 2 && ((k % 10) + carry) <= 5)
{
temp = 5 * power(10, nod) + temp;
carry = 0;
}
if (((k % 10) + carry) > 5 && ((k % 10) + carry) <= 9)
{
temp = 2 * power(10, nod) + temp;
carry = 1;
}
k = k / 10;
nod++;
}
if (carry == 1)
temp = 2 * power(10, nod) + temp;
k = temp;
return k;
}
int main()
{
long long int t;
cin >> t;
while (t--)
{
long long int l, r;
cin >> l;
cin >> r;
long long int sum = 0;
for (long long int i = l; i <= r; i++)
sum += f(i);
cout << sum << endl;
}
return 0;
}
但它没有按预期工作,对于许多测试用例,它超过了时间限制。 对于这个问题,什么是正确和最佳的方法?
这个问题来自hackerearth。
https://www.hackerearth.com/pt-br/problem/algorithm/ozs-cool-numbers-a97d4b77-4f0585e3/#:~:text=Special%20numbers%20are%20positive%20integers,n%20d%20265%20are%20not.&text=For%20each%20test%20case%2C%20print%20the%20required%20answer.&text=Time%20Limit%3A%201%2C0%20sec,s(%20for%20each%20input%20file.
我尝试过这个,它似乎有效。待同行评审。
一般的方法是找到适合您的起始号码之上的最小特殊数字,然后将该特殊数字乘以数字数量,直到您点击下一个特殊数字。由于两个特殊数字之间的每个数字都将映射到相同的特殊数字,因此您只需为每个范围计算一次每个特殊数字。
一个 64 位无符号整数可以表示仅由最多 19 位的 2 和 5 组成的十进制数。因此,对下一个特殊数字的每次搜索都是输入数字中十进制数字数的对数(以 2 为基数(。sum 循环的迭代次数在输入数字的十进制位数中也是对数(以 2 为底(,这使得总复杂度O(log 2N)
- 设置
sum = 0
- 给定起始编号
L
,设置next_k = L
- 求不小于
next_k
的最小特殊数;special = f(next_k)
- 设置
k = next_k
- 设置
next_k = min(special, R) + 1
.这是使用迭代中常见的"一过完"成语 - 设置
distance = next_k - k
sum = sum + distance * special
- 如果
next_k <= R
,则转到 3。 - 做。返回
sum
.
当然,如何在给定k
之上"安装"一系列 2 和 5 的问题需要回答。我的方法是从可以用 64 位(连续 19 个 5(表示的最大十进制 5 系列开始。
- 将此 5 掩码向下移动 10 倍,直到其宽度与输入数字相同
- 从最高有效数字开始,遍历数字并测试将该数字设置为 2 是否仍"适合"输入数字。如果是这样,请摆动它。
/*
A special number is a decimal number such that its
digits are only 2 or 5, e.g. {2, 5, 22, 25, 52, 55, ...}
Write a function, f( ), for any non-special number, k, that returns
the smallest special number that is greater than or equal to k
Given a range [L, R], find the sum of all f(k) for each k such
that L <= k <= R
*/
#include <algorithm>
#include <iomanip>
#include <iostream>
using value_t = unsigned long long;
static constexpr value_t MAX_FIVES = 5'555'555'555'555'555'555ull;
static constexpr value_t MAX_THREE_MASK = 3'000'000'000'000'000'000ull;
value_t least_special_no_less_than(value_t k)
{
value_t special_number = MAX_FIVES;
value_t three_mask = MAX_THREE_MASK;
if (k == 0)
{
special_number = 2;
}
else
{
// shift the 555... mask down as low as it can go
while (special_number / 10 >= k)
{
special_number /= 10;
three_mask /= 10;
}
// twiddle each digit from 5->2 to fit the sheet onto the bed
while (three_mask)
{
if (special_number - three_mask >= k)
special_number -= three_mask;
three_mask /= 10;
}
}
return special_number;
}
int main()
{
std::cout << "A special number is one with digits only 2 or 5, e.g. {2, 5, 22, 25, 55, 222, ...}n"
<< "For a number, k, f(k) returns the lowest special number gte kn"
<< "Enter two numbers L and R, this program calculates sum(f(k)) for all k s.t. L <= k <= Rn"
<< "n"
<< "Enter L followed by R: ";
value_t L, R;
std::cin >> L >> R;
value_t sum = 0;
value_t k, next_k = L;
std::cout << std::right
<< std::setw(20) << "k" << " "
<< std::setw(20) << "next_k" << " "
<< std::setw(20) << "special" << " "
<< std::setw(20) << "running sum" << " "
<< "n";
do
{
value_t special = least_special_no_less_than(next_k);
k = next_k;
next_k = std::min(special, R) + 1;
sum += (next_k - k) * special;
std::cout
<< std::setw(20) << k << " "
<< std::setw(20) << next_k << " "
<< std::setw(20) << special << " "
<< std::setw(20) << sum << " "
<< "n";
} while (next_k <= R);
std::cout << std::left
<< "nSum of all special numbers between [" << L << ", " << R << "]: "
<< sum << "n";
}
这给出了以下示例输出:
A special number is one with digits only 2 or 5, e.g. {2, 5, 22, 25, 55, 222, ...}
For a number, k, f(k) returns the lowest special number gte k
Enter two numbers L and R, this program calculates sum(f(k)) for all k s.t. L <= k <= R
Enter L followed by R: 12 47
k next_k special running sum
12 23 22 242
23 26 25 317
26 48 52 1461
Sum of all special numbers between [12, 47]: 1461
为了找到至少等于给定数字的下一个特殊数字,我将提取该数字的十进制数字。然后从最高阶数字开始:
如果一个数字大于 5,- 那么这个数字和所有后面的数字都会得到值 2,并且应该增加前一个数字(请注意,如果前一个数字是 5,我们需要迭代,如果我们在第一个数字,我们需要添加一个新数字(
- 如果数字是 5,请保留它并继续下一个 数字
- 如果为 3 或 4,则此值为 5,以下所有 值均为 2
- 如果为 2,请保留它并继续下一个
- 如果为 0 或 1,则此一个和所有后续值将为 2
出于效率原因,我会计算该数字,在当前数字小于或相等时将其相加,然后通过增加其最低有效数字来计算下一个特殊数字。
在下面的代码中,我选择将所有特殊数字处理封装在一个包含数字值向量和数字本身的类中:
#include <vector>
#include <iostream>
class Special {
public:
long val;
std::vector<char> digits;
// ctor builds first special number at least equal to val
Special(long val) {
std::vector <char> d;
int n=0;
while (val > 0) { // extract all decimal digits
d.push_back(val % 10);
val /= 10;
n++;
}
digits = std::vector<char>(n, 2); // initialize the special number with 2
// and start processing starting from most significant digit
while (n-- > 0) {
if (d[n] > 5) {
digits[n] = 6;
normalize(n);
return;
}
else if (d[n] > 2) {
digits[n] = 5;
if (d[n] != 5) {
normalize(digits.size());
return;
}
}
else {
digits[n] = 2;
if (d[n] != 2) {
normalize(digits.size());
return;
}
}
}
normalize(digits.size());
}
Special& normalize(int n) {
/* compute the binary value, and process carries starting from nth digit */
// First process carries
int ndigits = digits.size();
while (n < ndigits) {
if (digits[n] > 5) {
digits[n] = 2;
if (n == ndigits - 1) {
digits.push_back(2);
}
else {
digits[n+1] += 1;
}
}
else {
if (digits[n] > 2) {
digits[n] = 5;
}
else {
digits[n] = 2;
}
break;
}
n++;
}
// compute the binary value
val = 0;
for (auto ix=digits.rbegin(); ix!= digits.rend(); ix++) {
val = 10 * val + *ix;
}
return *this;
}
Special& next() {
/* increase to next special number */
digits[0] += 1; // just increase least significant digit
return normalize(0); // and normalize
}
};
int main() {
// read N
int N;
std::cin >> N;
long l,r, tot;
for (int i=0; i<N; i++) {
std::cin >> l >> r;
tot = 0;
Special s(l);
while(l <= r) {
if (l > s.val) s.next();
tot += s.val;
l += 1;
}
std::cout /* << l << " - " << r << " : " */ << tot << 'n';
}
return 0;
}
也许晚了,但我在这里留下来
对于这个问题,关键是要认识到序列是如何工作的。 只有两位数,2 和 5,这意味着该系列的行为为
- 2 --->创建 22 和 25
- 5 --->创建 52 和 55
- 22 --->创建 222 和 225
- 。 n --->分别创建 (10n +
- 2( 和 (10n + 5(
以下是我对使用堆栈以提高效率的问题的实现:
long long nthNiceNumber(int n)
{
queue<long long> q;
q.push(2); // init the stack with 2 and 5
q.push(5);
int nE = 2; // means the back of the queue is the n^th element
while (nE < n) // while doesn't reach the n^th element
{
q.push(q.front() * 10 + 2);
nE++;
if (nE == n) break; // in one iteration, the queue will have 2 new
// element,so if the first element appended is enough, break
q.push(q.front() * 10 + 5);
nE++;
q.pop(); // pop the front of the queue that used to create two new element above, since it doesn't relevant anymore
}
int size = q.size();
while (size-- > 1) q.pop(); // the n^th element always at the end of the queue, so pop until only one element left.
return q.front();
}
- 比较并显示使用最小值(a,b)和最大值(a、b)升序排列的4个数字
- 为什么随机数生成器不在void函数中随机化数字,而在main函数中随机化
- 检查输入是否不是整数或数字
- 如何(从固定列表中)选择一个数字序列,该序列将与目标数字相加
- 如何用数字处理log(0)
- 最高有效数字侧的第N位
- 如何获取一个数字的前3位
- 查找最接近的大于当前数字的数字的索引
- 找到两对数字,使它们的乘积的绝对差最小化
- 我想做一个彼此不同但重复出现的数字
- 将数字转换为字母(例如:123 转换为一二三)
- C++如何计算用户输入的数字中的偶数位数
- 只有 2 和 5 作为其数字的特殊数字
- 将一个数字拆分为多个数字,每个数字只有一个有效位
- 为什么数字类型只有"to_string()"?
- 如何确定字符串是否只有数字,没有字母
- 只有在没有重复数字的情况下才打印数字
- 在二叉树中插入4或5个数字,但输出中只有3个数字
- 用键盘输入数字到数组中,但只有一行
- 格式化的天气.TextCtrl只有数字值-浮点精度