美文网首页
CF414B Mashmokh and ACM

CF414B Mashmokh and ACM

作者: yuq329 | 来源:发表于2020-05-27 20:17 被阅读0次

CF414B Mashmokh and ACM

题目描述

Mashmokh's boss, Bimokh, didn't like Mashmokh. So he fired him. Mashmokh decided to go to university and participate in ACM instead of finding a new job. He wants to become a member of Bamokh's team. In order to join he was given some programming tasks and one week to solve them. Mashmokh is not a very experienced programmer. Actually he is not a programmer at all. So he wasn't able to solve them. That's why he asked you to help him with these tasks. One of these tasks is the following.

A sequence of ll integers b_{1},b_{2},...,b_{l}(1<=b_{1}<=b_{2}<=...<=b_{l}<=n) is called good if each number divides (without a remainder) by the next number in the sequence. More formally b_i | b_{i+1} for all i (1<=i<=l-1)(1<=i<=l−1) .

Given n and k find the number of good sequences of length k . As the answer can be rather large print it modulo 1000000007 (10^9+7).

输入格式

The first line of input contains two space-separated integers n,k (1<=n,k<=2000).

输出格式

Output a single integer — the number of good sequences of length k modulo 1000000007(10^{9}+7).

题意翻译

如果一个数列中,后一个数都能被前面一个数整除,那么就叫这个数列为好数列。输入n,k,求数列中最大元素为n,数列长度为k的好数列的种数(对1000000007取模)
由 @ROBOT233 提供翻译

输入输出样例

输入: 3 2 输出:5
输入: 6 4 输出:39
输入: 2 1 输出:2

说明/提示
In the first sample the good sequences are:

[1,1],[2,2],[3,3],[1,2],[1,3][1,1],[2,2],[3,3],[1,2],[1,3]

基本思路

使用动态规划,定义dp[k][i]表示1~i的序列中,以i结尾并且长度为k的好数列的种数,那么dp[k][i]与以j结尾(j<=i)且长度为k-1的好数列种数有关,即dp[k-1][i],好数列的条件既要求i%j==0.

for (int j = i; j > 0; --j)
    if (i % j == 0) dp[k][i] = (dp[k][i] + dp[k - 1][j]) % MOD;
状态转移图
//超时TLE
#include<iostream>

#define MOD 1000000007
#define ll long long
using namespace std;

ll dp[2001][2001];

int main() {
    int n, k;
    cin >> n >> k;
    for (int i = 1; i <= n; ++i) dp[1][i] = 1;

    ll ans = 0;
    for (int t = 2; t <= k; ++t)
        for (int i = 1; i <= n; ++i)
            for (int j = i; j > 0; --j)
                if (i % j == 0)
                    dp[t][i] = (dp[t][i] + dp[t - 1][j]) % MOD;
                
    for (int i = 1; i <= n; ++i) ans = (ans + dp[k][i]) % MOD;
    for (int t = 1; t <= k; ++t) {
        cout << t << ": ";
        for (int i = 1; i <= n; ++i)
            cout << dp[t][i] << ' ';
        cout << '\n';
    }
    cout << ans << '\n';

}

python3版本,超时

def solve():
    MOD = 1000000007
    n, k = list(map(int, input().strip().split(' ')))
    dp = [[0] * (n + 1) for _ in range(k + 1)]
    for i in range(1, n + 1):
        dp[1][i] = 1

    for t in range(2, k + 1):
        for i in range(1, n + 1):
            for j in range(i, 0, -1):
                if i % j == 0:
                    dp[t][i] = (dp[t][i] + dp[t - 1][j]) % MOD

    ans = sum(dp[k]) % MOD
    print(ans)

    for row in dp:
        print(row)


if __name__ == '__main__':
    solve()

可以发现,因为当前数应是前一个数的倍数,因为 j 循环可以使用倍数 来减少循环。

#include<iostream>

#define MOD 1000000007
#define ll long long
using namespace std;

ll dp[2001][2001];

int main() {
    int n, k;
    cin >> n >> k;
    for (int i = 1; i <= n; ++i) dp[1][i] = 1;

    ll ans = 0;
    for (int t = 2; t <= k; ++t) 
        for (int j = 1; j <= n; ++j) 
            for (int i = 1; i * j <= n; ++i) 
                dp[t][i * j] = (dp[t][i * j] + dp[t - 1][j]) % MOD;

    for (int i = 1; i <= n; ++i) ans = (ans + dp[k][i]) % MOD;

    for (int t = 1; t <= k; ++t) {
        cout << t << ": ";
        for (int i = 1; i <= n; ++i)
            cout << dp[t][i] << ' ';
        cout << '\n';
    }

    cout << ans << '\n';
}
#遗憾的是,python3版本仍然超时
def solve():
    MOD = 1000000007
    n, k = list(map(int, input().strip().split(' ')))
    dp = [[0] * (n + 1) for _ in range(k + 1)]
    for i in range(1, n + 1):
        dp[1][i] = 1

    for t in range(2, k + 1):
        for j in range(1, n + 1):
            for i in [i * j for i in range(1, n + 1) if i * j <= n]:
                dp[t][i] = (dp[t][i] + dp[t - 1][j]) % MOD

    ans = sum(dp[k]) % MOD
    print(ans)

    for row in dp:
        print(row)


if __name__ == '__main__':
    solve()

测试

输入:6 4
输出:
1: 1 1 1 1 1 1 
2: 1 2 2 3 2 4 
3: 1 3 3 6 3 9 
4: 1 4 4 10 4 16 
39

节约内存

由于动态规划过程中,只利用了二维矩阵某一行上的信息,即长度为k的好数列仅仅与长度为k-1的好数列有关,尝试将二维矩阵压缩成一维。

#include<iostream>

#define MOD 1000000007
#define ll long long
using namespace std;

ll dp[2001] = {0};
ll pre[2001] = {0};

int main() {
    int n, k;
    cin >> n >> k;
    for (int i = 1; i <= n; ++i) pre[i] = 1;

    ll ans = 0;
    for (int t = 2; t <= k; ++t) {
        for (int j = 1; j <= n; ++j) {
            for (int i = 1; i * j <= n; ++i) {
                dp[i * j] = (dp[i * j] + pre[j]) % MOD;
            }
        }
        for (int j = 1; j <= n; ++j) {
            pre[j] = dp[j];
            dp[j] = 0;
        }
    }

    for (int i = 1; i <= n; ++i) ans = (ans + pre[i]) % MOD;

    for (int i = 1; i <= n; ++i)
        cout << pre[i] << ' ';
    cout << '\n';

    cout << ans << '\n';
}

还可以进一步减少内存

#include<iostream>

#define MOD 1000000007
#define ll long long
using namespace std;

ll dp[2001] = {0};

int main() {
    int n, k;
    cin >> n >> k;
    for (int i = 1; i <= n; ++i) dp[i] = 1;

    ll ans = 0;
    for (int t = 2; t <= k; ++t) 
        for (int j = n; j > 0; --j) 
            for (int i = 2; i*j <= n; ++i) 
                dp[i * j] = (dp[i * j] + dp[j]) % MOD;

    for (int i = 1; i <= n; ++i) ans = (ans + dp[i]) % MOD;

    for (int i = 1; i <= n; ++i)
        cout << dp[i] << ' ';
    cout << '\n';

    cout << ans << '\n';
}

相关文章

网友评论

      本文标题:CF414B Mashmokh and ACM

      本文链接:https://www.haomeiwen.com/subject/zhtgahtx.html