划分数,分苹果问题·计算机算法·动态规划·C/C++

Hello,你好呀,这里是大千小熊,一个又会MMD又会C++的正派角色。B站同名UP主,CSDN同步更新,欢迎您的关注。

问题描述:

有N个苹果,M个盘子,问总共有多少种摆放的方法?(注意,盘子不分先后的顺序)

例如:4个苹果3个盘子总共有(1,1,2)(1,3,0)(2,2,0)(4,0,0)这4种方法。

1<=M<=n<=100

要求:使用动态规划解决这个问题。

题解

一开始我使用的状态方程是dp[i][j]=sum(dp[i-1][j-k]),意思就是说,先取出k个,然后剩下的再次进行分配。

但是答案是不正确的,因为(1,2,1)和(1,1,2)会被重复记数。

这个时候换了一种思路。

因为动态规划的本质是利用先前的结果再计算。所以建立一个真确的递推关系尤为重要。

假设我们现在的dp数组中所有的值都是正确的。

那么dp[i][j]的意思是,此时有i个盘子,j个苹果。


已盘子作为思考对象。因为盘子不存在先后顺序。只存在有么有,有多少个苹果的问题。

分为两个情况(1)没有一个盘子是空的。(2)至少有一个盘子是空的。

比如(2,2,0)最后一个盘子是空的。(1,1,2)没有盘子是空的。

所以当前的dp[i][j]等于“没有一个盘子是空的”+”至少有一个盘子是空的“。


那怎么做到第一个状态:没有一个盘子是空的呢?

那么首先,我在所有的盘子里面都放上一个苹果。那么现在还剩下n-m个苹果。(n是苹果的个数,m是盘子的数量)因为dp[m][n-m]是已经计算过的。(假设这个dp数组所有的值都是正确的),固然dp[m][n-m]里面肯定也有包含了空盘子的状况,但是由于我事先在所有的盘子里放了一个苹果,所以就算dp[m][n-m]有盘子为0,但是0+1还是大于0。满足”没有一个盘子是空的“

到目前为止递推式可以写成这样:dp[i][j]=dp[i][j-i]+至少一个空盘子。

那么至少一个空盘子怎么计算呢。

实际上,根据上文的分析,它就是dp[i-1][j],因为不考虑盘子的顺序,所以我首先随便空出一个盘子。然后dp[i-1][j]里面包含了空盘子的状况。所以最后空出的盘子是至少大于1的。那么到底空出了几个,这个不是我们能知道的问题了。

还差最后一步,边界和初始化

那么对于边界问题,有一个需要注意的点是:dp[0->M][0]=1。

为什么?因为在上述的递推关系中存在dp[i][j-i],这其中就可能j和i相等,那么这是上面意思呢。就是说此将每个苹果都放一个在盘子上面,放完刚好没有多余的。那么这当然是1种情况。

然后,如果只有1个盘子,那么苹果再多,也只有一种方法放苹果,所以最终的答案还是1。

所以dp[1][0->N]=1。

然而,您只需要初始化dp[0][0]=1也可以达到上面同样的效果。

下面的代码为您深深演示了这种思想,祝您享受愉快。

C++参考代码

#include <cstdio>
#include<iostream>
using namespace std;
long long N, M;
int dp[101][101];
int main() {
  scanf("%d", &N);
  scanf("%d", &M);
  dp[0][0] = 1;
  // for (int i = 0; i <= N; i++)
  //   dp[1][i] = 1;
  for (int i = 1; i <= M; i++) {
    for (int j = 0; j <= N; j++) {
      if (j < i) {
        dp[i][j] = dp[i - 1][j];
      } else {
        dp[i][j] = dp[i - 1][j] + dp[i][j - i];
      }
    }
  }
  for (int i = 1; i <= M; i++) {
    for (int j = 0; j <= N; j++) {
      cout << dp[i][j] << "  ";
    }
    cout << endl;
  }
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值