题目描述
将整数nn分成kk份,且每份不能为空,任意两个方案不相同(不考虑顺序)。
例如:n=7n=7,k=3k=3,下面三种分法被认为是相同的。
1,1,51,1,5;
1,5,11,5,1;
5,1,15,1,1.
问有多少种不同的分法。
输入输出格式
输入格式:
n,kn,k (6<n \le 2006<n≤200,2 \le k \le 62≤k≤6)
输出格式:
11个整数,即不同的分法。
输入输出样例
输入样例#1:
7 3
输出样例#1:
4
说明
四种分法为:
1,1,51,1,5;
1,2,41,2,4;
1,3,31,3,3;
2,2,32,2,3.
解法:
很直观的dfs。但题目要求是求不同的分法,所以这里学一个小技巧。
按照值的大小由小到大选取,每次dfs时记录上次选取的数。这个剪枝可以很好的避免超时。
#include <bits/stdc++.h>
#define pii pair<int, int>
#define ll long long
using namespace std;
const int maxn = 2e4 + 10;
int cnt;
int n, k;
set<multiset<int> > q;
multiset<int> a;
void solve(int last, int n, int k)
{
if(n < 0) return;
if(k == 0 && n != 0) return;
if(n == 0 && k == 0)
{
cnt++;
return;
}
for(int i = last; i * k <= n; i++)
{
solve(i, n - i, k - 1);
}
}
int main()
{
// freopen("/Users/vector/Desktop/testdata.in", "r", stdin);
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n >> k;
solve(1, n, k);
cout << cnt << endl;
return 0;
}
解法二:递推
dfs虽然好理解 但只能处理数据范围小的情况。如果数据范围一大就必须用递推的方法。
于是我们用状态dp[i][j]表示将i个数划分为j段的方案数。
我们可以将题目转化为将n个小球放入k个盒子中并且没有盒子为空的情况数。
所有的可能性等于至少有一个盒子有一个球和没有一个盒子有一个球。
这样进行划分是因为这种分类可以使都有能写出来的表达式
状态转移是从将i-1个球分为j-1个盒子和将i-j个球分为j段转移而来
dp[i][j] = dp[i - 1][j - 1] + dp[i - j][j]
#include <bits/stdc++.h>
#define pii pair<int, int>
#define ll long long
using namespace std;
const int maxn = 2e4 + 10;
int cnt;
int n, k;
int dp[200][200];//dp[i][j]把i分为j段
int main()
{
// freopen("/Users/vector/Desktop/testdata.in", "r", stdin);
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n >> k;
//递推
dp[1][1] = 1;
for(int i = 2; i <= n; i++)
{
dp[i][1] = 1;
for(int j = 2; j <= i; j++)
dp[i][j] = dp[i - 1][j - 1] + dp[i - j][j];
}
cout << dp[n][k] << endl;
return 0;
}