链接:https://ac.nowcoder.com/acm/problem/16695?&headNav=acm
来源:牛客网
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld
题目描述
将整数n分成k份,且每份不能为空,任意两份不能相同(不考虑顺序)。
例如:n=7,k=3,下面三种分法被认为是相同的。
1,1,5; 1,5,1; 5,1,1;
问有多少种不同的分法。
输入:n,k ( 6 < n ≤ 200,2 ≤ k ≤ 6 )
输出:一个整数,即不同的分法。
输入描述:
两个整数 n,k ( 6 < n ≤ 200, 2 ≤ k ≤ 6 )
输出描述:
1个整数,即不同的分法。
示例1
输入
7 3
输出
4
问题分析
DP法的理解:
把n分成k份,情况一,可以先拿出一个1,再把剩下的n-1分成k-1份。情况二,要让第一个数大于1,也就是2起步,因为题意要求的划分是没有顺序的,所以其它位置也是2起步,那么可以看成是先让每个位置都放一个1,然后再进行划分,也就是把n-k分成k份,因为是用dp法,这个问题在之前就处理过了,所以引用之前的答案就可以了。在此之前,要把子问题的答案求出来,对于把i(i<=n)划分成j(j<=k),那么除了上面的两种情况,还有j>i的情况和i == j的情况。j>i,无法划分,故取0;i == j,就是把i划分成j个1,所以答案是1.
综上,
如果i==j,dp[i][j]=1;
如果i<j,dp[i][j]=0;
如果i>j,dp[i][j]=dp[i-1][k-1]+dp[i-j][k].
DFS法的理解:
每个位置,都放一个加数add,对总数计数,且为了不重复,往后的加数只能越来越大或者不变。如果能这样填满k个空位,得到n,那么这样的安排就是合理的,对应方法数+1。但其实,在第k-1的位置填完会就可以知道第k个位置能放的值,所以可以提前判断能否划分成功,并且以sum+add<=n,保证,第k个位置放的数不小于前面放过的数,这样就不会出现重复了。
代码如下
//dp法
#include<bits/stdc++.h>
using namespace std;
int dp[205][10];
int main(){
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int n,k,i,j;
cin>>n>>k;
for(i=0;i<=n;i++)
for(j=0;j<=k;j++){
if(i==j) dp[i][i]=1;
else if(i<j) dp[i][j]=0;
else dp[i][j]=dp[i-1][j-1]+dp[i-j][j];
}
cout<<dp[n][k]<<endl;
return 0;
}
//dfs法
#include<bits/stdc++.h>
using namespace std;
int n,k,ans;
void dfs(int layer,int sum,int add){
if(layer==k-1&&sum+add<=n){
ans++;
return;
}
for(int i=add;i<=n;i++){
if(sum+i>=n) break;
dfs(layer+1,sum+i,i);
}
}
int main(){
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>n>>k;
ans=0;
dfs(0,0,1);
cout<<ans<<endl;
}