数的划分 题解

1. 深搜

轻而易举可以看出,本题转化为数学模型就是把一个大于 0 的整数 n 无序划分为 k 份的方案数

即求不定方程 x1+x2+x3+...+xk=n,1x1x2x3...xk 的解的个数

那就可以依次枚举 x1,x2,x3,...,xk 的值,再加以判断。

可是如果直接搜索的话,运行速度会很慢,所以我们要考虑剪枝。

我们发现,枚举 x1,x2,x3,...,xk 的值有以下两个多次重复的地方:上界与下界

1. 由于枚举 x1,x2,x3,...,xkx1,x2,x3,...,xk 值的重复概率很大,造成大量无效操作,所以我们考虑枚举依次递增

所以设 int f[9],该数组记录枚举的方案数,扩展第 i 个方案时的下界应是不小于前一个的值,即 fi1fi

2. 假设我们将 n 已经分解为了 f1,f2,f3...fi1,时,则 fi 的最大值应保证以后的 fi+1,fi+2...fn 都不下降,设 t=n(f1+f2+...+fi1) 所以 fi 的上界是 tki+1k 表示一共分成多少块,题面提到过)

所以我们就可以写出 AC Code 了:

#include<bits/stdc++.h>
#define int long long//重点:十年OI一场空,不开 long long 见祖宗(doge
using namespace std;
int ans,n,f[9]={0,1},k;//初始化f[1]=1,因为第一个数最低一定是1
inline void dfs(int p)
{
if(n==0) return;//如果n减完了,那就不用再减了
if(p==k)//如果累计加了k个数
{
if(n>=f[p]) ans++;//如果分完了,答案+1
return;
}
for(int i=f[p];i<=n/(k-p+1);i++)//确定上下界
f[p+1]=i,n-=i,dfs(p+1),n+=i;//保存状态,搜索,回溯
return;
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0); //读入优化(然而并没有什么用)
cin>>n>>k;
dfs(1);//搜索,从第一个数开始
cout<<ans;
return 0;
}

2. 计数 DP

由于题目中 6<n200,2k6 因此我们十分敏感地也可以设 fi,j 表示 i 个数,来分成 j 份的方案数

我们分类讨论:

1.i<j 时,数不够分,因此无解,但题目中说了不可能 i<j,因此可以省略

2.i=j 时,刚好一种情况,够分,需要 f0,0=1

3.i>j 时, 这是重点,我们考虑:

举个例子:假如现在有一个状态(k=3):{2,2,3}3 个数里面不含 1,我们就可以知道这个状态可以由 {1,1,2} 推出来,再由 fi,j 表示的含义,得 fi,j=fij,j+fi1,j1

综合以上,再加上初始化,得 AC Code2

//Or we can do this:
#include<bits/stdc++.h>
#define int long long
using namespace std;
int f[501][501];//f开全局,可以清零
signed main()
{
int n,k;
cin>>n>>k;
for(int i=1;i<=n;i++)
{
f[i][1]=1;
f[i][0]=1;
}//初始状态,根据常识可知:i个数选0或1个只有一种选法
for(int i=2;i<=n;i++)
for(int x=2;x<=k;x++)
if(i>x)//情况3
f[i][x]=f[i-1][x-1]+f[i-x][x];
else//剩下情况,迭代前面的
f[i][x]=f[i-1][x-1];
cout<<f[n][k];//n分成k个的方案数
return 0;
}

THE END


posted @   DreamerX  阅读(59)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示