每日一宏
#define 大法师 dfs
大法师万岁
题目
题目描述
将整数 n 分成 k 份,且每份不能为空,问有多少种不同的分法。当 n=7, k=3 时,下面三种分法被认为是相同的:1,1,5; 1,5,1;
输入格式
一行两个数 n , k。
输出格式
一行一个整数,即不同的分法数。
样例
输入
7 3
输出
4
思路
首先,题目要求把n分成k份
即
x
1
+
x
2
+
x
3
+
.
.
.
+
x
k
=
n
x1+x2+x3+...+xk=n
x1+x2+x3+...+xk=n
大法师的思路就是将所有方案枚举并进行判断是否将ans++
但是单纯的大法师是会超时的,所以我们要加以剪枝
根据题目,我们可以得出两个剪枝:
剪枝①
我们默认该方案是分解出的数列是升序排列
即
a
[
k
−
1
]
<
=
a
[
k
]
a[k-1]<=a[k]
a[k−1]<=a[k]
这样可以便于我们整理方案
剪枝②
假设我们已经分了i-1份,此时n剩下了m
即
m
=
n
−
(
a
[
1
]
+
a
[
2
]
.
.
.
+
a
[
i
−
1
]
)
m=n-(a[1]+a[2]...+a[i-1])
m=n−(a[1]+a[2]...+a[i−1])
接下来的第i份的最小值是a[i-1] (剪枝①)
最大值是m的剩下份数(k-i+1)的平均值
即
a
[
i
−
1
]
<
=
a
[
i
]
<
=
m
k
−
i
+
1
a[i-1]<=a[i]<={m\over k-i+1}
a[i−1]<=a[i]<=k−i+1m
如果a[i]超过m/(k-i+1)
那么在最后一次分的时候就会出现两种情况:
①:剩下的数比前一次分的数小
②:还没到第k份n就已经减完了
所以要在这个范围内进行搜索
AC代码
#include<iostream>
using namespace std;
int n,k;
int ans;
int a[205];
void dfs(int);
int main()
{
cin>>n>>k;
a[0]=1;//默认第一个数选择1,为了使k-1有值所以设a[0]=1
dfs(1);//从1开始
cout<<ans;
return 0;
}
void dfs(int i){//i指分到第i部分
if(!n){//n到0就停止搜索,因为已经没法分了
return;
}
if(i==k){
if(n>=a[k-1]){//当分到最后一次时剩下的n比上一次分的数大时,答案就已经有了,ans++停止搜索
ans++;
}
return;
}
for(int j=a[i-1];j<=n/(k-i+1);j++){
a[i]=j;//记录此次分的数
n-=j;
dfs(i+1);
n+=j;//回溯
}
}
暴力代码
可能想不出剪枝
那就相信大法师吧!
#include<iostream>
using namespace std;
int n,k;
int ans;
int a[205];
void dfs(int);
int main()
{
cin>>n>>k;
a[0]=1;
dfs(1);
cout<<ans;
return 0;
}
void dfs(int i){
if(i==k+1){
if(!n){
ans++;
}
return;
}
for(int j=a[i-1];j<=n;j++){
a[i]=j;
n-=j;
dfs(i+1);
n+=j;
}
}
数据有点奇怪,为什么暴力的大法师过了(小声