数的划分
来源:互联网 发布:筑巢软件上班怎么样 编辑:程序博客网 时间:2024/05/17 06:19
题目描述
Level 1
题目描述
把正整数N分解成M个正整数的和,即使M个数相同但顺序不同也认为是不同的方案,要求总方案数。如3=1+2跟3=2+1是两个不同的方案。
输入
第一行包含两个整数N和M(1<=M<=N<=50)。
输出
输出一个数表示方案数。
样例输入
3 2
样例输出
2
数据范围限制
1<=M<=N<=50
Level 2
题目描述
把正整数N分解成M个非负整数的和,即使M个数相同但顺序不同也认为是不同的方案,要求总方案数。如3=1+2跟3=2+1是两个不同的方案。
输入
第一行输入两个整数(1<=M<=N<=30)。
输出
输出一个整数表示方案数。
样例输入
2 3
样例输出
6
数据范围限制
1<=M<=N<=30
Level 3
题目描述
把正整数N分解成M个正整数的和,M个加数相同但顺序不同认为是相同的方案,要求总方案数。如3=1+2跟3=2+1是两个相同的方案。
输入
第一行输入两个整数N,M(1<=M<=N<=50)。
输出
输出一个整数表示方案数。
样例输入
5 3
样例输出
2
数据范围限制
1<=M<=N<=50
分析
level 1
搜索
拿到这道题我们通常会先用搜索的角度看待本题,我们可以分层依次枚举1-(n-1),再将每一组结果用sum累加。总方案累加的条件即为sum==n且枚举个数等于m.
代码实现如下
#include<iostream>#include<cstdio>using namespace std;int sum,m,n,cont;int a[105];void c(int q){ for(int i=1;i<=m;i++) if(sum+i<=m&&q<=n) { a[q]=i; sum+=a[q]; if(sum==m&&q==n){cont++;} c(q+1); sum-=a[q]; }}int main(){ cont=0; scanf("%d%d",&m,&n); c(1); printf("%d\n",cont);}
然而。。时间复杂度已经达到了O(mn)。也就是说输入50 50时需要循环2500次!很显然,这是超时的,因此需要用其他算法改进。
递推–记忆化搜索
我们从新整理思路,从生活实际的角度开始思考。
本题非常类似放苹果,因此我们可以以1为单位,将m中的一个一看成一个盘子,n中的一个一看成一个苹果,也就是说将n个苹果放入m个盘子里,求它在考虑顺序时的不同分法。相信大家都学过,像这种题可以用夹板法求解,因此我们可以轻松求出递推公式
边界条件是
代码实现如下
#include<iostream>#include<cstdio>using namespace std;long long f[55][55];long long put(int n,int m){ if(m==1)return 1; if(m==n)return 1; if(n<1)return 0; if(f[n][m])return f[n][m]; long long s=0,t=n-m+1; for(int i=1;i<=t;i++) s+=put(n-i,m-1); return f[n][m]=s;}int main(){ int s1,s2; scanf("%d%d",&s1,&s2); printf("%lld\n",put(s1,s2));}
这么一来这道题就AC了,但是这种方法仍然不是最优的,还可以变得更简单。
递推升级–杨辉三角
仔细观察下面这个杨辉三角。
有没有发现与本题的结果有相似之处呢?例如3行2列的数字2,实际上就是F[3][2]的答案!由此,我们又可以推出一个更简化的递推公式
边界条件只有
代码实现如下
#include<iostream>#include<cstdio>using namespace std;long long a[55][55],m,n;int main(){ int s1,s2; scanf("%d%d",&s1,&s2); a[1][1]=1; for(n=2;n<=s1;n++) for(m=1;m<=n;m++) a[n][m]=a[n-1][m]+a[n-1][m-1]; printf("%lld\n",a[s1][s2]);}
level 2
经过上一级的“长篇大论”,这一级就要简单许多了。
我们仍然从杨辉三角形讲起。这道题多了一个可以拆的数的存在–0。但也导致了n不一定会大于等于m。因此,这次的图不在是一个三角形了,而是一个长方形。
仍然观察本图,有没有发现与杨辉三角,与本题有很大的相似之处?
通过观察,我们会发现任意一点都等于它左边和上方两数之和。
因此,我们又可以推出递推公式
F[i][j]=F[i-1][j]+F[i][j-1]
边界是 F[1][j]=j,F[i][1]=1
代码实现如下
#include<iostream>#include<cstdio>using namespace std;long long a[55][55],m,n;int main(){ int s1,s2; scanf("%d%d",&s1,&s2); for(int j=1;j<=s2;j++)a[1][j]=j; for(int j=1;j<=s1;j++)a[j][1]=1; for(n=2;n<=s1;n++) for(m=2;m<=s2;m++) a[n][m]=a[n-1][m]+a[n][m-1]; printf("%lld\n",a[s1][s2]);}
level 3
题目又升级了!现在题目要求不考虑排列顺序了,那么,我么是否能用“图”解出来呢?
答案是肯定的,但是本题的规律复杂,数据范围小,并不如传统递归容易,如下图
与放苹果的递归相似,只需略加修改即可。
代码实现如下
#include<iostream>#include<cstdio>using namespace std;int a[55][55],m,n;int main(){ int s1,s2; for(n=0;n<=50;n++) { for(m=0;m<=50;m++) { if(m>n)a[n][m]=0; else if(m==1)a[n][m]=1; else if(n==0)a[n][m]=0; else a[n][m]=a[n-m][m]+a[n-1][m-1]; } } scanf("%d%d",&s1,&s2); printf("%d\n",a[s1][s2]);}
总结
这三道题总的来说都是通过递推,递归完成,但都可以借助图来帮助思维简化递推公式。此外,如果有更好的方法,可以在评论区进行讨论哦
- 数的划分
- 数的划分
- 数的划分问题
- 数的划分
- [NOIP2001]数的划分
- 数的划分
- 数的划分
- 数的划分
- 数的划分
- 数的划分
- 数的划分
- 数的划分
- codevs1039 数的划分
- 数的划分
- 数的划分
- NOIP2011:数的划分
- NOIP2001数的划分
- 【noip2001】数的划分
- 单选一项污染物
- 开发者可以使用Docker做什么?
- CSDN-markdown编辑器简单使用说明
- win10 安装搜狗输入法却无法正常使用解决方法
- 浅谈远程登录时,ssh的加密原理
- 数的划分
- 测试工具之charles学习笔记1
- JavaSE大纲以及学习网址
- Lottie源码简单分析以及使用
- codeforces 200 D Programming Language(stl)
- java 打印出如下图案(菱形)
- 关于线程与进程
- GDB No symbol "XXXXX" in current context
- sqlserver 截取两个固定字符串之间的值