数的划分

数的划分问题:

引言:将一个数n划分成m个部分和,问划分方案,划分出的各种部分的个数相等,总数相等视为一种方法,例如5=1+1+3,5=1+3+1视作一种。

第一步思路:首先要明白,如果说把n划分为m个,那么可以视作是先从n中拿一个x,题目可以变为将(n-x)分为m-1种,规模降低。

第二步思路:去重。当我们每次对这个数进行划分的时候,我们可以拿走一个x,然后将剩余部分分解为m-1种,但是如何防止不会发生重复的情况呢?

answer:我们试图保证每次拿的x'不会比上一次拿的x要小,并且每次我们只会拿一个1出来,也就是我们的x属于的范围为[1,m]。

不往上拿的原因:1.是为了保证x的非下降性,否则可能下一次拿的数的最大值都无法满足非下降性

                             2.将那些超过m的部分留在dp[i'][1]里,可以简化问题。

于是有递推式:dp[i][j]=dp[i-1][j-1]+dp[i-j][j]。 // dp[i][j]表示将i分解成j个部分

这样,考虑重复性就变成了如何考虑全有1的和没有1的情况。

有1的情况对应了dp[i-1][j-1],没有1的情况我们让dp[i-j][j]与之对应,理由是,我们可以把i-j看作是为j个元素预分配一个1,这样,最终的结果里不可能再有1。

或者简单来说:我们把数字i分解,那么最终的结果只可能是有1的和无1的两种情况而已。

实际上按照任意一个x分解都可以,但是用1可以保证不会越界,比如dp[5][4],如果每次都要找要2和不要2的话,很显然会出现dp[-1][1]这种数据。

 

 

把数字i分解的方案总数代码:

#include<iostream>
#include<cstring>
using namespace std;
int dp[15][15];
//可以理解为预先为每个数都分配一个1 这样就不会产生1了 
int main(){
    int t,v,sum;
    cin>>t;
    dp[0][0]=1;
    dp[1][1]=1;
    for(int i=1;i<=10;i++){
        for(int j=1;j<=i;j++){
            dp[i][j]=dp[i-1][j-1]+dp[i-j][j];
        }
    }    
    while(t--){
        cin>>v;
        sum=0;
        for(int i=1;i<=v;i++)sum+=dp[v][i];
        cout<<sum<<endl;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值