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;}