1. 程式人生 > >貝殼2018.10.15筆試 找尋序列

貝殼2018.10.15筆試 找尋序列

題目描述

在這裡插入圖片描述
n確認序列長度,m確定最後一個數。還有一個要求是(從提示可以明顯看出),每兩個數,後者要能整除前者,比如[1,3,3]中,【1,3】要3能整除1,【3,3】要3能整除3。

動態規劃

如果n=1那麼只有一種情況。
如果n>1,那麼有遞迴式,dp[n,m] = sum( dp[n-1][x] )。x為n所有可能的因數。
在這裡插入圖片描述
根據上圖來看,比如找【3,4】,4的因數有1,2,4,那麼就等於【2,1】【2,2】【2,4】的個數相加,如果看每個元素儲存的序列,那就是【2,1】【2,2】【2,4】裡面的各個序列後面再加個4。
解釋一下這個遞迴式:
1.第n個數字是m,第n-1個必須是m的因數x
2.已經確定了第n個數字,剩餘的前n-1個數的情況,已經被所有的dp[n-1][x]存起來了(因為找的是x,x又是m的因數,所以就可以滿足第n-1個是m的因數)
3.根據這樣分析遞推,那麼又可以得到第n-2個又是第n-1的因數。

#include <bits/stdc++.h>
using namespace std;
 
const int mod = 1e9 + 7;
typedef long long LL;
LL dp[1005][1005];
 
int main()
{
    for (int i = 1; i < 1005; i++) dp[1][i] = 1;
    for (int i = 2; i < 1005; i++) {      // 長度N
        for (int j = 1; j < 1005; j++) {  // 尾值M
            int tmp =
sqrt(j); for (int k = 1; k <= tmp; k++) { if (j % k == 0) { dp[i][j] += dp[i - 1][k]; dp[i][j] %= mod; dp[i][j] += dp[i - 1][j / k]; dp[i][j] %= mod; } } if
(tmp * tmp == j) { dp[i][j] -= dp[i - 1][tmp]; dp[i][j] %= mod; } } } int N, M; while (cin >> N >> M) { cout << dp[N][M] << endl; } return 0; }

程式碼來自牛客網大佬@HanKin,先在此鳴謝。解釋一下那個平方根,因為一個數的因數是成對出現的,我們只需要求到1到平方根的因數就行,另一半因數用原數除以因數就能得到。比如10,平方根是3,遍歷1,2,3。1是因數,10/1也是因數;2是因數,10/2也是因數。3不是因數。

滾動陣列

修改成py程式碼,使用滾動陣列,降低空間複雜度。

import math
n,m = map(int,input().split())

mod = 10**9 + 7
dp = [[0]*(m+1),[1]*(m+1)]

for i in range(2,n+1):
    lastrow = (i-1) % 2
    row = i % 2
    for j in range(1,m+1):
        temp = int(math.sqrt(j))
        dp[row][j] = 0
        for k in range(1,temp+1):
            if j%k == 0:
                dp[row][j] += dp[lastrow][k]
                dp[row][j] %= mod
                dp[row][j] += dp[lastrow][j//k]
                dp[row][j] %= mod
        if temp*temp == j:
            dp[row][j] -= dp[lastrow][temp]
            dp[row][j] %= mod

print(dp[n%2][m])

可以發現數組是重複利用的,但必須是[[0]*(m+1),[1]*(m+1)],不能只是[1]*(m+1),因為如果這樣,算dp[n,m] = sum( dp[n-1][x] )時,較小的x對應的dp[n-1][x]會被更新為dp[n][x],這樣等算到較大的x對應的dp[n][x]時,需要用到較小的x對應的dp[n-1][x],但此時較小的x對應的dp[n-1][x]已經被更新為dp[n][x],這樣算出來就不對了。