题目描述
将整数n分成k份,且每份不能为空,任意两个方案不相同(不考虑顺序)。
例如:n=7,k=3,下面三种分法被认为是相同的。
1,1,5;
1,5,1;
5,1,1.
问有多少种不同的分法。
输入输出格式
输入格式:
n,k (6<n≤200,2≤k≤6)
输出格式:
1个整数,即不同的分法。
输入输出样例
输入样例
7 3
输出样例
4
代码
- 代码一:
#include<iostream>
#include<cstdio>
using namespace std;
#define digit 10001
typedef unsigned long long ull;
ull record[digit]={1};
ull tot=0;
void search(ull n,ull k,ull time)
{
for(ull i=record[k-1];i<=n;i++)
{
if(i<=n&&k<=time)//剪枝一
{
record[k]=i;
n-=i;
if(n==0&&k==time) tot++;
else search(n,k+1,time) ;
n+=i;
}
}
}
int main()
{
ull n,time;
scanf("%lld%lld",&n,&time);
search(n,1,time);
printf("%lld",tot);
return 0;
}
第一次写的代码,经过一定的剪枝,可以得到80分,可见,时间复杂度依然没有达到题目的要求
题目分析(书上原话)
搜索的方法是依次枚举 x1,x2……xk 的值,然后判断。如果这样直接搜索,程序的运行速度是非常慢的。但由于本题的数据规模较小,如果控制好扩展节点的“上界”和“下界”,也是能很快得出解的。
约束条件
-
由于分解树不考虑顺序,因此我们设定分解数依次递增,所以扩展节点时的“下界”应是不小于前一个扩展节点的值,即 a[i-1]<=a[i]。
-
假设我们将 n 已经分解成了 a[1]+a[2]+…+a[i-1],则 a[i] 的最大值为将 i~k 这 k-i+1 份平均划分,即设 m=n-(a[1]+a[2]+…+a[i-1]),则 a[i]<=m/(k-i+1),所以扩展节点的“上界”是 m/(k-i+1)。
-
代码二
#include<cstdio>
#include<iostream>
using namespace std;
int n,m,record[8],tot=0;
void dfs(int k)
{
if(n==0)
return ;
if(k==m)
{
if(n>=record[k-1])
tot++;
return ;
}
for(int i=record[k-1];i<=n/(m-k+1);i++)
{
record[k]=i;
n-=i;
dfs(k+1);
n+=i;
}
}
int main()
{
scanf("%d%d",&n,&m);
record[0]=1;
dfs(1);
printf("%d",tot);
return 0;
}
经过抄袭蓝皮书的代码就得到了100分