问题描述
2018年清华软院推免考试(校外直博&校内硕/博)
第三题——同构数
如果一个数n满足:记n的位数为d(n),若n的各次幂的末d(n)位都与n相等,则称n为“同构数”。现在题目是,输入进制m和正整数k,求m进制下第k个同构数,例如10进制下第4个同构数是25(1,5,6,25)。这里约定所有进制的第一个同构数都是1.
规模约定:m大于5小于16,k小于21,保证结果存在且小于INT_MAX,但不保证中间计算过程不会超出INT_MAX
示例:
Input
6 2
Output
3
(因为是考后回忆,可能记忆有些模糊了,题干和代码都有可能有错,忘谅解)
------------------------------------------------------------
思路
笔者想到的方法是高精度+动态规划(类似的一个思路)。
注意到同构数的两个性质:
1. 如果一个数和它的平方结尾相等,则这个数和它的各次幂的结尾都相等,因此仅凭平方就能判定这个数是同构数
2. 一个n位同构数的末(n-1), (n-2), …,2, 1位也是同构数,这个性质决定了可以采用类似动态规划的方法大大减少计算量
所谓“类似动态规划的方法”是:
首先计算1位的同构数,保存在vector中;
2位的同构数的个位一定在vector中,因此只要枚举个位是vector中的数的十位数进行判断,将2位的同构数添加到vector中;
3位的同构数要么后两位是2位的同构数,要么第2位是0,个位是1位的同构数,再对符合条件的3位数枚举判断添加;
以此类推……
其实这道题打表完全是可以的,但由于清华软院机试不是OJ,怕老师会看代码,因此没敢打表。
另外就是本题可以在第一题的代码上修改,需要增加的代码量并不大。
------------------------------------------------------------
代码
#define _CRT_SECURE_NO_WARNINGS
//输入两个长度不超过200的正整数A,B,求A和B的乘积。保证输入的正整数不会以0开头,要求输出的正整数也不能以0开头
#include <cstdio>
#include <cstring>
#include<vector>
using namespace std;
class BigInteger {
public:
static const int maxn = 1010;
static int BASE;
BigInteger (void)
{
n = 1;
memset(digit, 0, sizeof(digit));
}
BigInteger (int one_digit)
{
n = 1;
memset(digit, 0, sizeof(digit));
digit[0] = one_digit;
}
BigInteger &operator=(const BigInteger &rhs) {
n = rhs.n;
for (int i = 0; i < n; i++) {
digit[i] = rhs.digit[i];
}
return *this;
}
int digit[maxn], n;
void readInput() {
static char buffer[maxn];
scanf("%s", buffer);
int len = strlen(buffer);
n = 0;
for (int i = len - 1; i >= 0; i--) {
digit[n++] = buffer[i] - '0';
}
}
void normalize(int i) {
n = i;
while (n > 1 && digit[n - 1] == 0) {
n--;
}
}
void clear0() {
n = 1;
memset(digit, 0, sizeof(digit));
}
void add(const BigInteger &rhs, BigInteger &result) const{
int i, si = 0;
result.clear0();
for (i = 0; i < n + rhs.n; i++)
{
result.digit[i] = si + digit[i] + rhs.digit[i];
result.digit[i+1] = result.digit[i] / BASE;
result.digit[i] = result.digit[i] % BASE;
}
result.normalize(n+rhs.n);
}
void sub(const BigInteger &rhs, BigInteger &result) const {//ensure *this >= rhs
int i, x = 0;
for (i = 0; i < n; i++) {
x += digit[i];
if (i < rhs.n) {
x -= rhs.digit[i];
}
bool borrow = false;
if (x < 0) {
x += BASE;
borrow = true;
}
result.digit[i] = x;
if (borrow) {
x = -1;
} else {
x = 0;
}
}
result.normalize(i);
}
void mul(const BigInteger &rhs, BigInteger &result) const {
for (int i = 0; i < n + rhs.n; i++) {
result.digit[i] = 0;
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < rhs.n; j++) {
result.digit[i + j] += digit[i] * rhs.digit[j];
if (result.digit[i + j] >= BASE) {
result.digit[i + j + 1] += result.digit[i + j] / BASE;
result.digit[i + j] %= BASE;
}
}
}
result.normalize(n + rhs.n);
}
void left_shift(int L)
{
int i;
for (i=n-1; i>=0; i--)
{
digit[i+L] = digit[i];
}
for (i=L-1; i>=0; i--)
{
digit[i] = 0;
}
n += L;
}
bool tail (const BigInteger &rhs) const { // ensure rhs >= *this
for (int i = 0; i < n; i++)
{
if (digit[i] != rhs.digit[i])
{
return false;
}
}
return true;
}
int compare(const BigInteger &rhs) const {
if (n != rhs.n) {
return n - rhs.n;
}
for (int i = n - 1; i >= 0; i--) {
if (digit[i] != rhs.digit[i]) {
return digit[i] - rhs.digit[i];
}
}
return 0;
}
void output() const {
bool started = false;
for (int i = n - 1; i >= 0; i--) {
putchar('0' + digit[i]);
}
putchar('\n');
}
int toBASE10() // switch to integer of BASE 10
{
int ret = 0, i, multiplier = 1;
for (i=0; i<n; i++)
{
ret += digit[i] * multiplier;
multiplier *= 10;
}
return ret;
}
};
int BigInteger::BASE;
int main() {
int m = 10, k = 4, cnt = 0, i = 0, j = 1;
scanf("%d%d", &m, &k);
BigInteger::BASE = m;
BigInteger A, B, C;
vector<BigInteger> v;
do {
if (i == 0)
{
for (j=1; j<m; j++)
{
A = BigInteger(j);
A.mul(A, C);
if (A.tail(C))
{
cnt++;
v.push_back(A);
}
if (cnt == k)
{
goto mark;
}
}
i++;
}
else
{
vector<BigInteger> pre(v);
for (j = 1; j<m; j++)
{
B = BigInteger(j);
B.left_shift(i);
for (vector<BigInteger>::iterator it = pre.begin(); it != pre.end(); it++)
{
B.add(*it, A);
A.mul(A, C);
if (A.tail(C))
{
cnt++;
v.push_back(A);
}
if (cnt == k)
{
goto mark;
}
}
}
i++;
}
}while (cnt < k);
mark: printf("%d", A.toBASE10());
return 0;
}