20241018每日一题洛谷P2386
普及 每日一题 信息学竞赛 1206:放苹果
把M个同样的苹果放在N个同样的盘子里,允许有的盘子空着不放,问共有多少种不同的分法?(用K表示)5,1,1和1,5,1 是同一种分法。
第一行是测试数据的数目t(0<=t<=20)。以下每行均包含二个整数M和N,以空格分开。1<=M,N<=10。
对输入的每组数据M和N,用一行输出相应的K。
看到题目第一眼,这不是排列组合吗?!
遂进行C A组合,未能找到严格的通项公式,只能递推寻找答案
思路如下:
对于m个苹果,n个盘子
首先考虑特殊情况:(实际上可以不用考虑)
m = 0 时:即没有苹果可以瓜分,有 0 种分法
n = 0 时:即没有盘子可以放置,有 0 种分法
m < 0 时:不存在实际情况,有 0 种分法
if (m==0) return 0; if (n==0) return 0;
考虑递归公式:
从n个盘子开始,对于分苹果的方法有两种:
不分给第n个盘子||分给所有的盘子
先解释第一种情况:
从第n个盘子开始,假如后面的所有盘子也都不给分苹果
这样的话将会导致最后没有办法安置苹果,则为 n = 0 时的特殊情况 返回0
解释第二种情况:
从m个苹果开始,每次都给所有的盘子都分一个苹果,假如后面都是这样操作
则必会有一次操作后使得 m <= 0 这时显然返回 0
if (m<=0) return 0;
当 m = 1 时:这时如果盘子不为 0 个,则有且仅有一种分法,返回 1
当 n = 1 时:这时如果苹果不为 0 个,则有且仅有一种分法,返回 1
if (m==1) return 1; if (n==1) return 1;
可以发现,在 m或n 等于 1 时递推已经有了返回值,则无需考虑 m或n 等于 0 时的情况
回过头我们来解释一下为什么会有这两种分苹果的方法
第一种方法不难想到,第二种方法可能有点困难
我们由下往上来考虑这个问题:
假设我们有一颗二叉树描述递归的过程
root节点为 (m,n) ,令左孩执行的操作为 (m,n) -> (m,n-1) ,右孩执行的操作为 (m,n) -> (m-n,n)
对整棵树的节点而言 :
所有左孩执行的操作的方向为一条形似于由左下贯穿右上的直线
所有右孩执行的操作的方向为一条形似于由右下贯穿左上的直线
当 n = 1 的时候,即我们只有一个盘子可以放我们剩下的苹果
递归执行右孩操作可以得到一条形似于由右下贯穿左上的直线,这条直线位于所有由右孩操作组成的直线集的底层
当 n = 2 的时候,即我们只有两个盘子可以放我们剩下的苹果
递归执行右孩操作也可以得到一条形似于由右下贯穿左上的直线,这条直线位于 n = 1 的直线的上一层
同理,可以得出 n =n 的时候,这条右孩操作直线为直线集的顶层,从最后一层的特殊性,往回推出第一层该如何操作
即root节点的右孩操作该如何执行
核心代码如下:
using namespace std; int count(int m, int n) { if (m < 0) return 0; if (m == 1) return 1; if (n == 1) return 1; return count(m-n, n) + count(m, n - 1); } int main() { int t; scanf("%d", &t); for (int i = 0; i < t; i++) { int m, n,ans=0; scanf("%d %d", &m, &n); printf("%d", count(m, n)); } return 0; }
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 全网最简单!3分钟用满血DeepSeek R1开发一款AI智能客服,零代码轻松接入微信、公众号、小程