2018京东C++开发工程师实习线上笔试编程题参考题解

AC了第一题第三题(第一题被个低级错误浪费了一大堆时间。。。),第二题dp推错了,一首凉凉献给自己。


第一题

大致题意

题意是说,给你一个n(1<=n<=100000),要你找出1~n这么多个数的最小公倍数。因为结果可能较大,对987654321取模。

思路

最小公倍数,那就很直接了,依次对每个数分解质因数,统计各个素因子的个数,最终这个素因子的个数取这n个数中的最大值。例如n取4,那么素因子2的个数就要取2,不然就不是最小公倍数了。由于n取值较大,怕每个数都分解一次质因数会超时,就先打了个素数筛,再来分解。
(看别人贴出来的代码,似乎直接分解+暴力乘,不使用素数筛+快速幂也能过?)

然后本人犯了个很低级很低级的错误,把最终结果用素因子乘以次数,一直卡10%,直到最后半小时才发现,立马赶一个快速幂取模出来才AC。。。记录一下,打一下自己的脸,给自己提个醒。

参考代码

尴尬,笔试时太紧张,忘记把AC的代码保存起来了。。。下面贴的是后来补的代码,由于题目没开出来,无法测试正确性,不确定有没有bug,大家参考一下思路即可。。

#include <iostream>
#include <array>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <string>
#include <cstdio>
#include <set>
#include <vector>
#include <queue>
#include <stack>
#include <map>
#include <cctype>

using namespace std;

typedef long long LL;

const int MAXN = 1e5+5;
const LL MOD = 987654321;

array<LL, MAXN> prime;
array<bool, MAXN> isPrime;
array<LL, MAXN> num;
int pn;

void init() {
    isPrime.fill(true);
    isPrime[1] = false;

    pn = 0;
    for (int i=2; i<MAXN; ++i) {
        if (isPrime[i]) {
            prime[pn++] = i;
            for (int j=i*2; j<MAXN; j+=i) {
                isPrime[j] = false;
            }
        }
    }
}

LL quickPow(LL a, LL b) {
    LL ans = 1;

    while (b) {
        if (b & 1) ans = ans*a % MOD;
        a = a*a % MOD;
        b >>= 1;
    }

    return ans;
}

LL solve(int n) {
    init();

    num.fill(0);

    LL ans = 1;
    for (int i=2; i<=n; ++i) {
        int tmp = i;
        LL cnt = 0;
        for (int j=0; j<pn&&tmp>1; ++j) {
            while (tmp % prime[j] == 0) {
                ++cnt;
                tmp /= prime[j];
            }
            num[prime[j]] = max(num[prime[j]], cnt);

            if (isPrime[tmp]) {
                num[tmp] = max(num[tmp], 1LL);
                break;
            }
        }
    }

    for (int i=0; i<pn; ++i) {
        if (num[prime[i]]) {
            ans *= quickPow(prime[i], num[prime[i]]);
            ans %= MOD;
        }
    }

    return ans;
}

int main() {
    std::ios::sync_with_stdio(false);

    int n;
    cin >> n;
    cout << solve(n) << "\n";

    return 0;
}

第二题

题意

给你一个字符串(长度小于等于50),这个字符串移除掉若干个字符(可以移除0个字符)后,若变成回文串(不能是空串),就是一种可行的方案。如果两种方法移除的字符按顺序排列后不相同,就认为这是两种不同的方案。问最终共有几种方案。

思路

笔试的时候区间dp弄错了,看牛客网讨论区才明白。
设dp[i][j] 为区间[i, j]的方案数,则有

d p [ i ] [ j ] = { d p [ i ] [ j 1 ] + d p [ i + 1 ] [ j ] d p [ i + 1 ] [ j 1 ] , if str[i] != str[j] d p [ i ] [ j 1 ] + d p [ i + 1 ] [ j ] + 1 , if str[i] == str[j]

仔细琢磨一下递推公式就可以理解,当左右端点不同时,就等于左边这部分加上右边这部分,然后因为中间交叉的部分在左右都算了一次,所以减掉一次;当左右端点相同时,就是在不相同的基础上,加上中间部分(dp[i+1][j-1]),然后还有加上1,因为本身也算一个,化简一下就变成上面的公式。(ps:markdown支持latex公式,写出来的式子就是漂亮)

参考代码

#include <iostream>
#include <array>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <string>
#include <cstdio>
#include <set>
#include <vector>
#include <queue>
#include <stack>
#include <map>
#include <cctype>

using namespace std;

typedef long long LL;

int main() {
    std::ios::sync_with_stdio(false);

    string str;

    cin >> str;
    LL dp[55][55];

    memset(dp, 0, sizeof(dp));
    int len = str.length();

    for (int i=0; i<len; ++i)
        dp[i][i] = 1;

    for (int t=1; t<len; ++t) {
        for (int i=0; i<len; ++i) {
            int j = i+t;
            if (j >= len) break;

            dp[i][j] = dp[i][j-1] + dp[i+1][j] - dp[i+1][j-1];
            if (str[i] == str[j]) {
                ++dp[i][j];
                if (t > 1)
                    dp[i][j] += dp[i+1][j-1];
            }
        }
    }

    cout << dp[0][len-1] << "\n";

    return 0;
}

第三题

(看到题目一大张中国象棋棋盘,让我想起了许久没下的象棋, 在象棋信念的支持下很快就AC)

题意

题目先介绍了象棋中的马可以走8个方向,然后棋盘左下角为坐标原点,向上为y轴正方向,向右为x轴正方向。输入是一个整数k(k<=100000),以及一个坐标x,y(0<=x<=8, 0<=y<=8),问,当马一开始在坐标原点时,走k步之后能够停留在点(x, y)处的不同走法。最终结果对1000000007取模。

思路

这个数据范围,还要取模,很明显就是要dp了。马在一个点上可以走8个方向,那么也就意味着,对于棋盘上的每个点,最多有8个点能够走一步到达。那么本次该点的结果就是把这8个合法的点的值加起来取模就行了。循环k次即可得到答案。因为直接改变当前数组的值的话,会导致后续的结果不正确,所以我用了一个临时数组tmp来保存该次的结果,全部求出来之后在赋值回去。看了一下别人的代码,他们把两个数组合起来,用dp[9][9][2]来运算,每次让最后一维对1异或,就能保证两次的结果不会互相干扰,这样写了之后代码会变得很简洁,值得学习。这种方法我也写出来,放在参考代码2那里。

参考代码1

#include <iostream>
#include <array>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <string>
#include <cstdio>
#include <set>
#include <vector>
#include <queue>
#include <stack>
#include <map>
#include <cctype>

using namespace std;

typedef long long LL;

const LL MOD = 1000000007;
const int MAXN = 9;

int dirx[] = {-2, -1, 1, 2, 2, 1, -1, -2};
int diry[] = {1, 2, 2, 1, -1, -2, -2, -1};

LL dp[MAXN][MAXN];
LL tmp[MAXN][MAXN];

bool isIn(int x, int y) {
    return x >= 0 && x < MAXN && y >= 0 && y < MAXN;
}

LL solve(int k, int x, int y) {
    memset(dp, 0, sizeof(dp));
    dp[0][0] = 1;

    for (int i=0; i<k; ++i) {
        for (int r=0; r<MAXN; ++r) {
            for (int c=0; c<MAXN; ++c) {
                tmp[r][c] = 0;
                for (int p=0; p<8; ++p) {
                    int tx = r+dirx[p];
                    int ty = c+diry[p];
                    if (isIn(tx, ty)) 
                        tmp[r][c] = (tmp[r][c] + dp[tx][ty]) % MOD;
                }
            }
        }

        for (int r=0; r<MAXN; ++r) {
            for (int c=0; c<MAXN; ++c) {
                dp[r][c] = tmp[r][c];
            }
        }
    }

    return dp[x][y];
}

int main() {
    std::ios::sync_with_stdio(false);

    int k;
    int x, y;

    cin >> k >> x >> y;
    cout << solve(k, x, y) << "\n";

    return 0;
}

参考代码2

(这是简洁一点的代码)

#include <iostream>
#include <array>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <string>
#include <cstdio>
#include <set>
#include <vector>
#include <queue>
#include <stack>
#include <map>
#include <cctype>

using namespace std;

typedef long long LL;

const LL MOD = 1000000007;
const int MAXN = 9;

int dirx[] = {-2, -1, 1, 2, 2, 1, -1, -2};
int diry[] = {1, 2, 2, 1, -1, -2, -2, -1};

LL dp[MAXN][MAXN][2];

bool isIn(int x, int y) {
    return x >= 0 && x < MAXN && y >= 0 && y < MAXN;
}

LL solve(int k, int x, int y) {
    memset(dp, 0, sizeof(dp));
    dp[0][0][0] = 1;

    int cur = 0;
    for (int i=0; i<k; ++i) {
        cur ^= 1;
        for (int r=0; r<MAXN; ++r) {
            for (int c=0; c<MAXN; ++c) {
                for (int p=0; p<8; ++p) {
                    int tx = r+dirx[p];
                    int ty = c+diry[p];
                    if (isIn(tx, ty)) 
                        dp[r][c][cur] = (dp[r][c][cur] + dp[tx][ty][cur^1]) % MOD;
                }
            }
        }
    }

    return dp[x][y][cur];
}

int main() {
    std::ios::sync_with_stdio(false);

    int k;
    int x, y;

    cin >> k >> x >> y;
    cout << solve(k, x, y) << "\n";

    return 0;
}

总结

总结起来,还是自己菜啊,第一题bug找太久(因为测试的时候只测了小数据,每个素数的个数不超过2),第二题简单的dp没想清楚,也就第三题顺了点。还需要多练,记录一下,共勉。

猜你喜欢

转载自blog.csdn.net/feng964497595/article/details/79874008