1. 程式人生 > >2018京東C++開發工程師實習線上筆試程式設計題參考題解

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]的方案數,則有

dp[i][j]={dp[i][j1]+dp[i+1][j]dp[i+1][j1],if str[i] != str[j]dp[i][j1]+dp[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沒想清楚,也就第三題順了點。還需要多練,記錄一下,共勉。