Codeforces Problem 414B. Mashmokh and ACM

题目链接: http://codeforces.com/problemset/problem/414/B  

题意: A sequence of l integers b1, b2, ..., bl (1 ≤ b1 ≤ b2 ≤ ... ≤ bl ≤ n) is called good if each number divides (without a remainder) by the next number in the sequence. More formally for all i (1 ≤ i ≤ l - 1).

            接收用户输入的两个整数 n, k (1 ≤ n, k ≤ 2000),从 1~n 中选择 k 个数满足前一个数能被后一个数整除 即bi+1 / bi = n  (n >= 1),求这样的序列总共有多少个。因为数据可能会比较大,要对 1e9+7 取余。

思路:

          从个位往最高位进行分析,可以发现:当 k = 1 (即序列只有1个整数) 的时候,num(序列个数) = n; 当 k = 2 时, 便于讨论,可以假设 n = 5, 因为后一个数是前一个数的整数倍,故应该从最后往前推,那么最后一位可以为 1, 2, 3, 4, 5 。 因为它后面没有数字,我们讨论 k - 1 位,在这里就是第一位,第一位(用 b1 表示)也可以是1, 2, 3, 4, 5 。当 b1 = 1 时, b2 = 1, 2, 3, 4, 5 ; 当 b1 = 2 时, b2 = 2, 4; 当 b1 = 3 时, b2 = 3;当 b1 = 4 时, b2 = 4; 当 b1 = 5 时, b2 = 5; 故 num =  5+2+1+1+1 = 10。 再序列再增加一位, k = 3 的时候, 当 b1 = 1 的时候, b2 = 1, 2, 3, 4, 5 ,情况同 k = 2, 故此时 num 个数为 10 ; 当 b1 = 2 时, b2 = 2, 4 ,相应的 b1 = 2, 4, 4 ,即当 b2 = 2 || 4 时候的和,同理 b1 = 3, 4, 5 。这是个递归的过程。

递归实现代码:

 

/* ***
* @Codeforce problem 414B.  Mashmokh and ACM
* @author: ckmoonfish
* @Date:2014.4.11
* @combinatories, dp
******/

#include <stdio.h>
#define mod 1000000007

int num = 0;
void dps(int t, int k, int n){
    int i = 0;
    if(k == 0){
        num = (num+1) % mod;       // 表示创建完一个序列
        //printf("i=%d, t=%d, k=%d, n=%d, num=%d\n", i, t, k, n, num);
    }
    else
        for(i = t; i <= n; i += t){
            //printf("%d ", i);
            dps(i, k-1, n);    // 从t,2*t,3*t <= n 一个个的找序列
    }
}
int main(){
    int j, n, k;
    scanf("%d%d", &n, &k);
    for(j = 1; j <= n; j++){  j 代表 序列中的第一个数
        dps(j, k-1, n);
    }
    printf("%d\n", num);
    return 0;
}

根本就没有题目标签的思想:DP 。交上去肯定就 TLE 了。

DP 思路: DP就是一个逆推的过程,用能简单求出的已知项向未知项推进,在推进的过程记录下求出的每一个状态的值,并在求未知项的过程中,只考虑前一步的值。

根据上面对 k = 1, 2, 3 的的分析知道: b1 = 1 时, numk3 = (b2= 1, 2, 3, 4, 5); b1 = 2 时, numk3 = (b2 = 2, 4);b1 = 3  时, numk3 = (b2 = 3; b1 = 4  时, numk3 = (b2 = 4);b1 = 5  时, numk3 = (b2 = 5);那么 k = 4 的时候呢? b1 = 1 时, numk4 = ( b2= 1,  2,  3,  4,  5 ) 。 同理可以推 k = 5, 6, ... 符合我们上面的 DP 思路。

可以写出 递推 公式: d[k-1][i] = d[k][i] + d[k][2*i] + d[k][3*i] + ... + d[k][p*i]  ;其中 p*i <= n   。

代码:

/* ***
* @Codeforce problem 414B.  Mashmokh and ACM
* @author: ckmoonfish
* @Date:2014.4.11
* @combinatories, dp
******/

#include <stdio.h>
#include <string.h>
#define MAXN 2014
#define mod 1000000007

int ins[MAXN][MAXN];
int main(){
    int i, j, p, n, k, num, st[MAXN];

    memset(ins, 0, sizeof(ins));
    memset(st, 0, sizeof(st));
    num = 0;

    scanf("%d%d", &n, &k);
    for(i = 1; i <= n; ++i)
        st[i] = n / i;
    for(i = 1; i <= n; i++)
        ins[k][i] = 1;

    for(i = k-1; i > 0; i--){
        for(j = 1; j <= n; j++){
            for(p = j; p <= n; p += j)
                ins[i][j] = (ins[i][j] + ins[i+1][p]) % mod;
           // printf("ins[%d][%d] = %d\n", i, j, ins[i][j]);
        }
    }

    for(i = 1; i <= n; ++i){
        num = (num + ins[1][i]) % mod;
    }

    printf("%d", num % mod);
    return 0;
}

注意取模,当数据很大的时候,容易超范围。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值