时间限制: 1000 ms 内存限制: 65536 KB
【题目描述】
将整数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、递归:递归函数每次将整数n分出一个数i,剩下的n-i进入下一次递归。
为了避免出现相同方案,每次分出的数不比上一个分出的数大,即分出的数按升序排列。
2、动规:
题目转换为更加形象具体的叙述:有n个苹果需要划分到k个盘子里面,不允许有空盘,不考虑顺序。
dp[i][j]表示将i个苹果放到j个盘子里的不同分配方法的个数。
将n个苹果放入k个盘子,首先要保证k个盘子不为空,即默认每个盘子初始有1个苹果,那么就需要划分剩下的n-k个苹果。
分为以下情况:
(1)n-k个苹果都放到k个盘子里,dp[n-k][k];
(2)n-k个苹果都放到k-1个盘子里,dp[n-k][k-1];
......
(k)n-k个苹果都放到1个盘子里,dp[n-k][1]。
所以,可以得出通式 dp[n][k] = dp[n-k][k] + dp[n-k][k-1] + dp[n-k][k-2] + ... + dp[n-k][1]。
但是,根据通式又可以推出, dp[n-1][k-1] = dp[n-k][k-1] + dp[n-k][k-2] + ... + dp[n-k][1]。
两式结合,最终可以得出,dp[n][k] = dp[n-k][k] + dp[n-1][k-1],即状态转移方程!
根据dp[i][j]的含义,显然 dp[1][j] = 1,dp[i][1] = 1。(j <= i)
上述的推导过程简单来说就是:
将n个苹果放到k个盘子中的情况总数 = 至少有一个盒子只有一个小球的情况数 + 没有一个盒子只有一个小球的情况数
(1)至少有一个盒子只有一个小球,因为盒子不加区分,那么情况数与“将n-1个小球放到k-1个盒子中”的情况数一样;
(2)没有一个盒子只有一个小球,那么把每个盒子中拿出来一个小球,对应的是“把(n-k)个小球放到k个盒子中的情况数”。
#include<bits/stdc++.h>
using namespace std;
//【递归】
int n, k;
int sum;
void f(int n, int k, int start)
{
if(n == 0 || k == 0)
{
if(n == 0 && k == 0)
{
sum++;
}
return ;
}
for(int i = start; i <= n; i++)
{
if(n - i < 0)
{
return ;
}
f(n - i, k - 1, i);
}
}
int main()
{
cin >> n >> k;
f(n, k, 1);
cout << sum;
}
(动态规划中强大的逻辑思维能力真的很重要,看着大佬的代码研究了半天......)
#include<bits/stdc++.h>
using namespace std;
//【动规】
int dp[205][10];
int n, k;
int main()
{
cin >> n >> k;
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= k && j <= i; j++)
{
if(i == 1 || j == 1)
{
dp[i][j] = 1;
}
else
{
dp[i][j] = dp[i - j][j] + dp[i - 1][j - 1];
}
}
}
cout << dp[n][k];
}