読者です 読者をやめる 読者になる 読者になる

tookunn’s diary

主に競技プログラミング関係

yukicoder No390 最長の数列

やったこと

まず最初に考えたのは「よい数列」というのはa[i] < a[i + 1](1 <= i <= N - 1)であるので、与えられる集合Sを昇順にして、dp[i][j]:= i番目までの要素を見て、最後に割り切れた要素がj番目の要素である時の良い数列の最大の長さ としてメモ化再帰をしましたが、
計算量はO(N^2)であるので、TLEでした。

N = int(input())
x = sorted([int(i) for i in input().split()])
dp = [[-1 for j in range(N)] for i in range(N)]
def dfs(i,v):
    if i == N:
        return 0
    if dp[i][v] != -1:
        return dp[i][v]
    ret = 0
    if x[i] % x[v] == 0:
        ret = dfs(i + 1,i) + 1
    ret = max(ret,dfs(i + 1,v))
    dp[i][v] = ret
    return ret
ans = 0
print(dfs(0,0))

次はdp[i] := i番目まで見た時に作ることができる良い最大の数列の長さ としてDPをしました。
それによって、dpを二次元配列から一次元配列にできた。(もともとあんまり効率良くないことやってたので、当たり前ですね)
i番目の要素ごとにi~1まで見ていくと、だいたいO(N^2)ぐらいなので、これもTLE。

N = int(input())
x = sorted([int(i) for i in input().split()])
dp = [0 for i in range(N)]
for i in range(N):
    for j in range(i,-1,-1):
        if x[i] % x[j] == 0:
            dp[i] = max(dp[i],dp[j] + 1)
print (max(dp))

そして、実際の解法はdp[i] := 値iを良い数列の終端としたときの、構成することが出来る良い数列の最大の長さ
dp[i] = max(dp[j] + 1) 値jは値iの約数、つまりj <= j * 2 <= j * 3 ... j * (i / j) = i

ソースコード

N = int(input())
x = sorted([int(i) for i in input().split()])
M = max(x) + 1
dp = [0] * M
ans = 0
for i in range(N):
	dp[x[i]] = 1
for i in range(N):
	for j in range(x[i] * 2,M,x[i]):
		if dp[j] == 0:
			continue
		dp[j] = max(dp[j],dp[x[i]] + 1)
	ans = max(ans,dp[x[i]])
print (ans)