苹果放盘子问题

2019.6.5
苹果放盘子问题,有盘子若干,苹果若干,盘子与苹果都完全相同,例如7个苹果,放入3个盘子,允许盘子为空,共有多少种方法?115和511是相同的方法,7个苹果3个盘子,答案是8种。

<一>暴力枚举
分析:手动枚举防止重复的方法就是通过有序排列,而选择从大到小的排序方法效率更高。
优点: 可以改造成可以打印出所有的排序情况的代码
注意点:++c表示的是指针所指向的数字加1,而c++表示的指针的位置向后移一位

#include<stdio.h>
void solve(int a,int b,int* c,int last)
{
   if(b==1)
   {
      if(a<=last&&a>=0) ++*c;
       return;
   }
   if(a==0){++*c;return;}
   
   int minn=last;
   if(a<minn)minn=a;
   for(int i=minn;i>0;i--)solve(a-i,b-1,c,i);
}
int main(void)
{
    int apple,dish,ans=0;
    scanf("%d %d",&apple,&dish);
    solve(apple,dish,&ans,apple+1);
    printf("共有%d种排序方法",ans);
    return 0;
}

改造成可打印排列的如下

#include<stdio.h>
int min(int a,int b){if(a<b)return a;  return b;}
void print(int arr[],int cnt)
{
    for(int i=0;i<cnt;i++)printf("%d ",arr[i]);
    printf("\n");
}
int solve(int a,int b,int* c,int last,int arr[],int cnt)
{
   if(b==1)
   {
      if(a<=last&&a>=0){++*c;arr[cnt]=a;print(arr,cnt+1);return 1;}
       return 0;
   }
   if(a==0){++*c;print(arr,cnt);return 1;}
   for(int i=min(a,last);i>0;i--)
    {
        arr[cnt]=i;
          solve(a-i,b-1,c,i,arr,cnt+1);
}
}
int main(void)
{
    int apple,dish,ans=0,arr[100];
    scanf("%d %d",&apple,&dish);
    solve(apple,dish,&ans,apple+1,arr,0);
    printf("共有%d种排序方法",ans);
    return 0;
}


<二>抓住本质写递归 --------from 硕硕
作者思路:首先是当盘子数目大于苹果数目时,多余的盘子是没有用的,所以这时候可以把多于的盘子拿走。当盘子数小于等于苹果数时,可以分为两种情况,有一个空盘子即有0存在的情况,跟所有盘子中都有苹果的情况。对于有0存在的情况,这个盘子存在和不存在并不影响分类数目,可以直接把这个盘子拿走。对于每个盘子中都有苹果的情况,从每个盘子中各拿走一个苹果后,也不影响分类数目。最后当没有苹果或者只有一个盘子的时候就是一种情况即(0个苹果b个盘子)(a个苹果1个盘子)。

我的理解:只有2个选择,要么处理苹果,要么处理盘子,处理苹果意味着把当前的盘子都放上一个苹果,处理盘子意味着减少一个盘子,去看少一个盘子的问题怎么处理,也就是另一个递归了。递归终止就是盘子数为1或者没有苹果了。

优点:运行速度快,效率高,比第一种方法快的多。

#include<stdio.h>
int solve(int a,int b)
{
    if(a==0||b==1)return 1;
    if(a<b)return solve(a,b-1);
    else return solve(a,b-1)+solve(a-b,b);
}
int main(void)
{
    int a,b;
    scanf("%d %d",&a,&b);
    int ans=solve(a,b);
    printf("%d",ans);
}

<三>递推公式
思路:偶然看到某位dl博客用的是递推公式。

#include<stdio.h>
int main(void)
{
    int a,b;
    scanf("%d %d",&a,&b);
    int all[100][100];
    for(int i=0;i<=a;i++)
    for(int j=0;j<=b;j++)
    {
     if(i==0||j==1){all[i][j]=1;continue;}
        if(i>=j)all[i][j]=all[i-j][j]+all[i][j-1];
    else all[i][j]=all[i][j-1];
    }
    printf("%d ",all[a][b]);
    return 0;
}


测试代码放这里了
https://github.com/Mally-cj/mine/commit/9d463e9afd15e32a7b6c55fb6c46aed719a310ed

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

是Mally呀!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值