ZOJ 1163

数论+DP

数的划分

dp(k, n),表示数 n 的最小被加数不小于 k 的分划的个数。对于给定的 k 值,dp(k, n) 正好分为以下两类:

1最小被加数等于 k
2最小被加数大于 k

满足第一个条件的分划的个数是 dp(k + 1, n − k)。 这是因为,让我们想象数 n − k 的最小被加数大于 k 的分划,然后将 "+ k" 附加每一个分划后面,就得到数 n 的最小被加数等于 k 的分划。以 n = 8, k = 1 为例,数 7 的最小被加数大于 1 的分划是 7、5 + 2 和 4 + 3,即 dp(k + 1, n − k) = dp(2, 7) = 3。然后,将 "+ 1" 附加在这 3 个分划后面,就得到数 8 的最小被加数等于 1 的分划:7 + 1、5 + 2 + 1 和 4 + 3 + 1。

满足第二个条件的分划的个数是 q(k + 1, n) 。以 n = 8, k = 1 为例,数 8 的最小被加数大于 1 的分划是 8、6 + 2 和 5 + 3,即 dpk + 1, n) = dp(2, 8) = 3。

也就是说,dp(1, 8) = dp(2, 8) + dp(2, 7)。因此:

        dp(k, n) = 0  如果 k > n
        dp(k, n) = 1  如果 k = n
        dp(k, n) = dp(k+1, n) + dp(k + 1, n-k);

#include <iostream>
using namespace std;
#define max 505
typedef long long ll;
ll dp[max][max];
void getdp(){
    for(int i=1;i<=max;i++)
    {
        dp[i][i]=1;
        for(int j=i+1;j<max;j++)
        dp[i][j]=0;
    }
    for(int i=2;i<max;i++)
        for(int j=i-1;j>=1;j--)
            dp[i][j]=dp[i][j+1]+dp[i-j][j+1];
}
int main(){
    int t;
    getdp();
    while(cin>>t){
            if(!t)break;
        cout<<dp[t][1]-1<<endl;
    }
    return 0;}

posted @ 2014-07-25 09:35  Mr.XuJH  阅读(155)  评论(0编辑  收藏  举报