数的划分
描述
将整数n分成k份,且每份不能为空,任意两份不能相同(不考虑顺序)。
例如:n=7,k=3,下面三种分法被认为是相同的。
1,1,5; 1,5,1; 5,1,1;
问有多少种不同的分法。 输出:一个整数,即不同的分法。
输入
两个整数n,k (6 < n <= 200,2 <= k <= 6),中间用单个空格隔开。
输出
一个整数,即不同的分法。
样例输入
7 3
样例输出
4
提示
四种分法为:1,1,5;1,2,4;1,3,3;2,2,3。
【题目 http://noi.openjudge.cn/ch0206/8787/】
分析
显然是个动规
方程:f[i,j]=f[i-1,j-1]+f[i,j-i]
这题可以看成把N个苹果放在K个盘子里,每个盘子都不能为空(即每个盘子里至少有一个苹果),求方案数
设f[i,j]表示j个苹果放在i个盘子的方案数
初始化很好理解,不管把几个苹果放在一个盘子里都只有一种放法,所以f[1,i]:=1;
好啦,初始化解释完就不管了
现在来看f[i,j]=f[i-1,j-1]+f[i,j-i];
假设现在盘子全为空,苹果都不在盘子里,我们开始放苹果吧!
(1):先在一个盘子里放一个苹果,就剩下j-1个苹果放在i-1个盘子里,有f[i-1,j-1]种放法
(2):再把盘子里的苹果全都拿出来(让盘子全为空),然后再开始放。 这次先在一个盘子里放两个苹果,就剩下j-2个苹果放在i-1个盘子里,有#种放法(先不管‘#’)是多少
(3):再把盘子里的苹果全都拿出来(让盘子全为空),然后再开始放。 这次先在一个盘子里放三个苹果,就剩下j-3个苹果放在i-1个盘子里,有#种放法(不管‘#’)是多少
……
以此类推,直到在一个盘子里放j-i+1个苹果,然后把这些方案数加起来,存在f[i,j]。
先在一个盘子里放一个苹果的方案数已经知道了,就先让f[i,j]=f[i-1,j-1]+%&#@,先不管%&#@是什么,回顾一下前面,从2开始就不好求了,因为你不知道当你先在一个盘子里放M(M>1)个苹果后求出来的方案有没有和前面重复。
那就换种思路来求除了(1)以外的方案数吧
看看题目的约束条件,即每个盘子里至少有一个苹果,为了满足条件,就让每个盘子里都有一个苹果,然后剩下j-i个苹果放在i个盘子里(有个盘子只有一个苹果的情况在(1)已经考虑过了,所以现在每个盘子至少要有两个苹果),而j-i个苹果放在i个盘子里的方案数已经算出来了(f[i,j-i]),每个方案都保证往当前的每个盘子中放至少一个苹果,当前每个盘子里已经都有一个苹果,所以这样一来每个盘子至少有两个苹果,(2)就包括在里面了。以此类推,(3),(4)……也都包括在(如果可能)f[i,j-i]里。所以加上f[i,j-i]就好了。
pascal代码
program sdfg; var n,k,i,j:longint; f:array[0..200,0..200] of longint; begin readln(n,k); for i:=1 to n do f[i,1]:=1; for i:=1 to n do for j:=2 to i do f[i,j]:=f[i-1,j-1]+f[i-j,j]; writeln(f[n,k]); end.
C++代码
#include <algorithm> #include <iostream> #include <cstdlib> #include <cstring> #include <string> #include <cstdio> #include <queue> #include <ctime> #include <cmath> int f[210][210]; int main() { // freopen("numf.in","r",stdin); // freopen("numf.out","w",stdout); int n,k,i,j; scanf("%d%d",&n,&k); for (i=1;i<=n;i++) f[i][1]=1; for (i=1;i<=n;i++) for (j=2;j<=i;j++) f[i][j]=f[i-1][j-1]+f[i-j][j]; printf("%d",f[n][k]); return 0; }
这是我第一次写题解,写的不好多多包涵