大致题意:
给出一个n和k,让你从 1 -- n 这n个数中,选出 k个(可重复)组成一个序列,使这个序列满足任意一个数都能够整除该序列中它前面的那个数。求这样的序列的个数。
分析:
由于每一个数都与其前面的一个数直接相关,每个数必须是其前面一个数的倍数。那么我们可以定义一个跟末尾数字有关的状态,我们用dp[ i ] [ j ] 表示长度为 i 并且最后一位是 j 的序列 ,那么状态转移方程:dp [ i ] [ j ] = sum(dp [ i-1 ] [ x ] ) x 是 j 前面满足( j % x == 0)的数。
因为是多组测试,在开始的时候直接打表可以节省时间。
比较雷的是,刚开始写的循环是
for(int i=1;i<2010;i++)
{
for(int j=1;j<2010;j++)
{
for(int q=1;q<=j;q++)
{
if(j%q==0)
{
dp[i][j] += dp[i-1][q];
dp[i][j] %= MOD;
}
}
}
}
换成这样会好一点。至少就不会T了。。对于每一个 i ,都有从1开始的 j 去控制的 q ,来增加dp [ i ] [ ] ,这样一圈循环下来,可以完全更新~
for(int i=1;i<2010;i++)
{
for(int j=1;j<2010;j++)
{
for(int q=j;q<2010;q=q+j)
{
dp[i][q] += dp[i-1][j];
dp[i][q] %= MOD;
}
}
}
#include #include #define MOD 1000000007 int dp[2010][2010]; int main() { int n,k,sum; for(int i=1;i<2010;i++) { dp[1][i]=1; } for(int i=1;i<2010;i++) { for(int j=1;j<2010;j++) { for(int q=j;q<2010;q=q+j) { dp[i][q] += dp[i-1][j]; dp[i][q] %= MOD; } } } while(scanf("%d%d",&n,&k)!=EOF) { sum=0; for(int i=1;i<=n;i++) { sum=sum+dp[k][i]; sum %= MOD; } printf("%d\n",sum); } }