题解 P1025 【数的划分】

· · 题解

这道题有两个思路

第一个是递归:

为了确保出现过的方案不重复,

可以规定在后面的分组中的数必须要大于前面分组中的数,

x代表上一个出现过的数,初值为1,只要让下一个数从x开始循环,便可达成上述方案。

s代表还需多少次递归,初值为k,递归k次,即分为k组后便可退出循环。

t代表到此次还剩多大的数可以分,初值定为n。

同时循环最大只能进行到t/s,

避免出现因前面的数过大而导致后面的数无法取的情况。

代码如下:

        #include<cstdio>
        using namespace std;
        int sum;
        void dfs(int x,int s,int t)
        {
            if(s==1)
            {
                sum++;
                return;
            }
            for(int i=x;i<=t/s;i++)
                dfs(i,s-1,t-i);
        }
        int main()
        {
            int n,k;
            scanf("%d %d",&n,&k);
            dfs(1,k,n);
            printf("%d",sum);
            return 0;
        }

第二个是递推: solution【i】【j】代表在数的和为i的情况下分为j组共有的情况数。

由于无论在数的和为几时,把它们分成0组都是0种情况,分成一组都是一种情况,

所以j=0时全部要初始化为0,j=1时全部要初始化为1。

同时,无论共分为几组,数的和为0和1是0种情况(i=j=1除外)

所以i=0或i=1时j从2开始全部初始化为0。

对于剩下的任意一个solution【i】【j】,都可以用如下方式求得:

solution【i】【j】=第一个数为1的所有情况+第一个数不为1的所有情况。

第一个数为1时,1占用了1个位置和占用了总数中的1是已经确定了的,

因此,第一个数为1的所有情况=solution【i-1】【j-1】。

第一个数不为1时,可以视为先在所有的位置上都加上一个1再对于所有的位置用新的总数求次数,

所以定了的是占有了总数j个,位置仍然是j个,与原来相比没有变化。

因此,第一个数不为1的所有情况=solution【i-j】【j】。

所以solution【i】【j】=solution【i-1】【j-1】+solution【i-j】【j】。

代码如下:

        #include<cstdio>
        using namespace std;
        int main()
        {
            int solution[205][10];
            int n,k,i,j;
            scanf("%d %d",&n,&k);
            for(i=1;i<=n;i++)
            {
                solution[i][1]=1;
                solution[i][0]=0;
            }
            for(i=2;i<=k;i++)
            {
                solution[1][i]=0;
                solution[0][i]=0;
            }
            for(i=2;i<=n;i++)
                for(j=2;j<=k;j++)
                    if(j>i)
                        solution[i][j]=0;
                    else
                        solution[i][j]=solution[i-1][j-1]+solution[i-j][j];
            printf("%d",solution[n][k]);
            return 0;    
}