一、问题描述
有 \(n\) 个相同的物品,将它们划分成 \(m\) 组,有几种划分方法。
注:以下划分都算一种:
1 + 1 + 2
1 + 2 + 1
2 + 1 + 1
二、问题简析
本题采用动态规划求解。令 \(dp[i][j]=\) \(i\) 的 \(j\) 划分的方案数。值得注意的是,本题根据是否可以有 \(0\) 存在,即允许某一组的值为 \(0\),分成两种情况。
允许 \(0\) 存在
设数列 \(\{a_n\}\) 为 \(dp[i][j]\) 的方案,则 \(\sum_{n=1}^ja_n=i\)。
数列 \(\{a_n\}\) 存在两种情况:
- 没有 \(0\),即 \(a_n>0\)。
如何得到这个数列呢?我们可以对数列 \(\{a_n-1\}\) 的每个元素+1
。数列 \(\{a_n-1\}\) 的元素和 \(\sum_{n=1}^j=i-j\),即 \(dp[i-j][j]\) 的方案。 - 存在 \(0\)
我们将这个 \(0\) 从数列中分离出来,数列的元素个数变成了 \(j-1\),然而元素之和仍然为 \(i\)。现在的数列是 \(dp[i][j-1]\) 的方案。
综上所述,\(dp[i][j]=dp[i-j][j]+dp[i][j-1]\)。
边界条件:
- 条件一:\(0\) 允许存在,所以 \(0\) 的任意划分(大于 \(0\))都为 \(1\)。\(dp[0][j]\) 为 \(j\) 个 \(0\)。
- 条件二:任意数(包含 \(0\))的 \(1\) 划分都是自身。
注:此时,\(dp[0][1]=1\)
不允许 \(0\) 存在
设数列 \(\{a_n\}\) 为 \(dp[i][j]\) 的方案,则 \(\sum_{n=1}^ja_n=i\)。
有两种情况对 \(dp[i][j]\) 有贡献:
- 在 \(dp[i-j][j]\) 的方案基础上,每个元素
+1
,就得到了不含 \(0\) 的 \(dp[i][j]\)。这就是上文提到的情况一。 - 在 \(dp[i-1][j-1]\) 的方案基础上,加入元素 \(1\)。与上文情况二唯一不同的地方就是,此处添加元素 \(1\),上文添加元素 \(0\)。
综上所述,\(dp[i][j]=dp[i-j][j]+dp[i-1][j-1]\)。
边界条件:
- 条件一:\(0\) 不允许存在,所以 \(0\) 的任意划分(大于 \(0\))都为 \(0\)。
- 条件二:任意数(不包含 \(0\))的 \(1\) 划分都是自身。
注:此时,\(dp[0][1]=0\)
三、例题
3.1 本题代码
本题不允许 \(0\) 存在。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n, k;
ll dp[205][10]; // dp[i][j] -- i的j划分
int main()
{
#ifdef LOCAL
freopen("test.in", "r", stdin);
#endif
cin >> n >> k;
// 边界条件一:dp[0][1,2,...]=0
// ... // 不允许0存在,所以0的所有划分都为0
// 边界条件二:dp[1,2,...][1]=1
for (int i = 1; i <= n; ++i) // i的1划分,即自身
dp[i][1] = 1;
for (int i = 1; i <= n; ++i) // i=0已经在条件一中初始化
{
for (int j = 2; j <= k; ++j) // j=1已经在条件二中初始化
{
if (i >= j)
dp[i][j] = dp[i - j][j] + dp[i - 1][j - 1];
// if i < j, dp[i][j] = 0 (因为不能有0存在)
}
}
cout << dp[n][k] << endl;
return 0;
}
3.2 改题目:允许 \(0\) 存在
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n, k;
ll dp[202][10];
int main()
{
#ifdef LOCAL
freopen("test.in", "r", stdin);
#endif
cin >> n >> k;
// 边界条件一:允许0存在,任意划分都为1
for (int j = 1; j <= k; ++j)
dp[0][j] = 1;
// 边界条件二:i的1划分就是自身
for (int i = 0; i <= n; ++i)
dp[i][1] = 1;
for (int i = 1; i <= n; ++i) // i=0在条件一中初始化
{
for (int j = 2; j <= k; ++j) // j=1在条件二中初始化
{
if (i >= j)
dp[i][j] = dp[i - j][j] + dp[i][j - 1];
else // i<j时,在i个1的基础上,添j-i个0
dp[i][j] = 1;
}
}
cout << dp[n][k] << endl;
return 0;
}
完
标签:...,数列,int,划分,允许,dp From: https://www.cnblogs.com/hoyd/p/18188822