首页 > 试题广场 >

小易喜欢的数列

[编程题]小易喜欢的数列
  • 热度指数:11194 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 32M,其他语言64M
  • 算法知识视频讲解
小易非常喜欢拥有以下性质的数列:
1、数列的长度为n
2、数列中的每个数都在1到k之间(包括1和k)
3、对于位置相邻的两个数A和B(A在B前),都满足(A <= B)或(A mod B != 0)(满足其一即可)
例如,当n = 4, k = 7
那么{1,7,7,2},它的长度是4,所有数字也在1到7范围内,并且满足第三条性质,所以小易是喜欢这个数列的
但是小易不喜欢{4,4,4,2}这个数列。小易给出n和k,希望你能帮他求出有多少个是他会喜欢的数列。

输入描述:
输入包括两个整数n和k(1 ≤ n ≤ 10, 1 ≤ k ≤ 10^5)


输出描述:
输出一个整数,即满足要求的数列个数,因为答案可能很大,输出对1,000,000,007取模的结果。
示例1

输入

2 2

输出

3

TLE方法(回溯):

public class Main {

    private static int ans = 0;
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n, k;
        n = sc.nextInt();
        k = sc.nextInt();
        boolean[] visited = new boolean[n+1];
        backtracking(new ArrayList<Integer>(), n, k);
        System.out.println(ans);
    }

    private static void backtracking(ArrayList<Integer> list, int n, int k){
        if(n==0){
            ans++;
            ans = ans % 1000000007;
            return;
        }
        for(int i = 1;i<=k;i++){
            if(list.size()==0 || (list.size()!=0 && (list.get(list.size()-1)<=i || list.get(list.size()-1)%i!=0))) {
                list.add(i);
                backtracking(list, n - 1, k);
                list.remove(list.size()-1);
            }
        }
    }
}

思路:

state[i][j]表示整个状态空间,其中i(1<=i<=n)表示数列的长度,j(1<=j<=k)表示数列长度为i且以数字j结尾。
递推关系有:state[i][j] += state[i-1][m] (1<=m<=k, 并且(m,j)是个合法的数列),但是直接按照递推关系,用三层for循环会超时。为此可以先将长度为i-1的合法数列求和(记为sum)。然后对于数列长度为i的每一个j,求出数列长度为i-1时非法的序列个数(记为invalid),即有state[i][j] = sum - invalid。
对于invalid求取,可以参照素数筛选。算法的时间复杂度大概为O(nkloglogk)

TLE方法(DP):

public class Main {
    static final int mod = 1000000007;

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n, k;
        n = sc.nextInt();
        k = sc.nextInt();
        int[][] state = new int[n+1][k+1];

        state[0][1] = 1;
        for(int i = 1;i<=n;i++){
            for(int j=1;j<=k;j++){
                for(int m=1;m<=k;m++){
                    if(m<=j || m%j!=0)
                        state[i][j]+=state[i-1][m];
                }
            }
        }
        int sum = 0;
        for(int i = 1;i<=k;i++){
            sum = (sum+state[n][i])%mod;
        }
        System.out.println(sum);
    }
}

AC方法:

public class Main {
    static final int mod = 1000000007;
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n, k;
        n = sc.nextInt();
        k = sc.nextInt();
        int[][] state = new int[n+1][k+1];

        state[0][1] = 1;
        for(int i = 1;i<=n;i++){
            int sum = 0;
            for(int j = 1;j<=k;j++){
                sum = (sum+state[i-1][j])%mod;
            }
            for(int j=1;j<=k;j++){
                int invalid = 0;
                int  p = 2;
                //满足的条件为: A<=B || A%B!=0
                //则不满足的条件为: A>B && A%B==0
                //此处即为 (p*j,j)
                while (p*j<=k){
                    invalid = (invalid+state[i-1][p*j])%mod;
                    p++;
                }
                state[i][j] = (sum-invalid+mod)%mod;
            }
        }
        int sum = 0;
        for(int i = 1;i<=k;i++){
            sum = (sum+state[n][i])%mod;
        }
        System.out.println(sum);
    }
}
编辑于 2019-08-02 22:30:49 回复(0)
//用动态规划做,列出动态规划式子

import java.util.*;

public class Main{
    public static final int mod = 1000000007;
    public static void main(String[ ] args){
        Scanner input = new Scanner(System.in);
        int n = input.nextInt();
        int k = input.nextInt();
        int[][] temp = new int[n+1][k+1];
        
        /*[0 1 2       (k-1) k]
        0  0 1 0             0
        1  0 
          
         
        (n-1)
        n
        */
        
        //动态规划,其中 invaild = temp[i-1][A]   A即为满足A mod B ==0
        //temp[i][j] = invalid-(temp[i-1][1] +.....+ temp[i-1][k])
        
        int sum, invalid, z;
        temp[0][1] = 1; //使得n=1时,各k列能初始化为1
        for(int i=1;i<=n;i++){
            sum = 0;
            for(int j=1;j<=k;j++){
                sum = (sum + temp[i-1][j]) % mod;
            }
            for(int j=1;j<=k;j++){
                invalid = 0;
                z = 2;
                while(j*z<=k){
                    invalid = (invalid + temp[i-1][***od;
                    z++;
                }
                temp[i][j] = (sum - invalid + mod) % mod;  //防止为负数
            }
        }
        sum = 0;
        for(int j=1;j<=k;j++){
            sum = (sum + temp[n][j]) % mod; //最后的结果为最后一行所有可能的和
        }
        System.out.println(sum);
    }
}

发表于 2019-03-18 21:35:14 回复(0)
import java.util.Scanner;

public class Problem_201703 {

    private static final int MOD = 1000000007;

    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        while (scan.hasNext()) {
            int n = scan.nextInt();
            int k = scan.nextInt();
            //dp[i][j]表示长度为i的序列以j结束数字的数列个数
            //dp[i][j] = dp[i - 1][m] (1 <= m <= k)并且(m,j)是一个有效的序列
            int[][] dp = new int[n + 1][k + 1];
            dp[0][1] = 1;
            for (int i = 1; i <= n; i++) {
                int sum = 0;
                //所有可能组合
                for (int j = 1; j <= k; j++) {
                    sum = (sum + dp[i - 1][j]) % MOD;
                }

                for (int j = 1; j <= k; j++) {
                    //删除所有不满足条件的情况,类似素数筛选的过程
                    int invalid = 0;
                    int p = 2;
                    while (p * j <= k) {
                        //dp[i - 1][p * j]违反了A % B != 0,因此剔除
                        invalid = (invalid + dp[i - 1][p * j]) % MOD;
                        p++;
                    }
                    //为初始化添加增量
                    dp[i][j] = (sum - invalid + MOD) % MOD;
                }
            }
            int res = 0;
            for (int i = 1; i <= k; i++) {
                res = (res + dp[n][i]) % MOD;
            }
            System.out.println(res);
        }
        scan.close();
    }

}
发表于 2018-08-10 16:19:46 回复(0)