动态规划
f[i][j]
表示数列的长度为i
, 并且以数j
结尾的合法序列的数量。
f[i][j] += f[i-1][l], 其中l,j满足合法序列的要求
//暴力法:时间:O(N*K*K), 空间:O(NK)
//40%通过
import java.util.*;
public class Main {
static int mod = 1000000007;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt(), k = sc.nextInt();
int[][] f = new int[n+1][k+1];
Arrays.fill(f[1], 1); f[1][0] = 0;
for(int i=2; i <= n; i++) {
for(int j=1; j <= k; j++) {
for(int l=1; l <= k; l++) {
if((l <= j) || (l % j != 0)) {
f[i][j] = (f[i][j] + f[i-1][l]) % mod;
}
}
}
}
//System.out.println(Arrays.deepToString(f));
int res = 0;
for(int i=1; i <= k; i++) res = (res + f[n][i]) % mod;
System.out.println(res);
}
}
优化:
条件为:(l <= j) || (l % j != 0)
,满足条件的数量明显较多,因此,我们用满足和不满足的和减去不满足的数量,就等于满足条件的数量。取反条件变成为:l % j == 0 && l != j
。
取反集以后求f[i][j]
枚举的数量从k
下降到了k/j
。k + k / 2 + k / 3 +...+k/k = k *(1 + 1/2 + ... + 1 /k) 约等于 k * ln(k+1) + 0.5, 因此,问题复杂度从O(K*K)
下降到了O(K*logK)
。
// 时间:O(NKlogK)
import java.util.*;
public class Main {
static int mod = 1000000007;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt(), k = sc.nextInt();
int[][] f = new int[n+1][k+1];
Arrays.fill(f[1], 1); f[1][0] = 0;
for(int i=2; i <= n; i++) {
int sum = 0;
for(int j=1; j <= k; j++) sum = (sum + f[i-1][j]) % mod;
for(int j=1; j <= k; j++) {
f[i][j] = sum;
for(int l=j; l <= k; l = l + j) {
if(l!= j) {
f[i][j] = (f[i][j] + mod - f[i-1][l]) % mod;
}
}
}
}
//System.out.println(Arrays.deepToString(f));
int res = 0;
for(int i=1; i <= k; i++) res = (res + f[n][i]) % mod;
System.out.println(res);
}
}