|
|
package 第01期.mca_08_dp;
|
|
|
|
|
|
// 有m个同样的苹果,认为苹果之间无差别
|
|
|
// 有n个同样的盘子,认为盘子之间也无差别
|
|
|
// 还有,比如5个苹果如果放进3个盘子,
|
|
|
// 那么1、3、1和1、1、3和3、1、1的放置方法,也认为是一种方法
|
|
|
// 如上的设定下,返回有多少种放置方法
|
|
|
// 测试链接 : https://www.nowcoder.com/practice/bfd8234bb5e84be0b493656e390bdebf
|
|
|
// 请同学们务必参考如下代码中关于输入、输出的处理
|
|
|
// 这是输入输出处理效率很高的写法
|
|
|
// 提交以下的code,提交时请把类名改成"Main"
|
|
|
import java.io.BufferedReader;
|
|
|
import java.io.IOException;
|
|
|
import java.io.InputStreamReader;
|
|
|
import java.io.OutputStreamWriter;
|
|
|
import java.io.PrintWriter;
|
|
|
import java.io.StreamTokenizer;
|
|
|
import java.util.Arrays;
|
|
|
|
|
|
public class Code09_SplitApples {
|
|
|
|
|
|
public static void main(String[] args) throws IOException {
|
|
|
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
|
|
|
StreamTokenizer in = new StreamTokenizer(br);
|
|
|
PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
|
|
|
while (in.nextToken() != StreamTokenizer.TT_EOF) {
|
|
|
int m = (int) in.nval;
|
|
|
in.nextToken();
|
|
|
int n = (int) in.nval;
|
|
|
out.println(ways3(m, n));
|
|
|
out.flush();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 思路来自于分裂数问题
|
|
|
// 体系学习班代码第22节,题目3,split number问题
|
|
|
public static int ways1(int apples, int plates) {
|
|
|
return process1(1, apples, plates);
|
|
|
}
|
|
|
|
|
|
// pre : 上一个盘子分到的苹果数量,当前的盘子分到的数量不能小于pre
|
|
|
// apples : 剩余的苹果数量
|
|
|
// plates : 剩余的盘子数量
|
|
|
// 在盘子够用的情况下,把苹果分完,有几种方法
|
|
|
public static int process1(int pre, int apples, int plates) {
|
|
|
if (apples == 0) {
|
|
|
return 1;
|
|
|
}
|
|
|
// apples != 0
|
|
|
if (plates == 0) {
|
|
|
return 0;
|
|
|
}
|
|
|
// apples != 0 && plates != 0
|
|
|
if (pre > apples) {
|
|
|
return 0;
|
|
|
}
|
|
|
// apples != 0 && plates != 0 && pre <= apples
|
|
|
int way = 0;
|
|
|
// 之前的盘子分了3个苹果,现在还剩下8个苹果
|
|
|
// 当前的盘子,可以装几个苹果:3、4、5、6、7、8
|
|
|
for (int cur = pre; cur <= apples; cur++) {
|
|
|
way += process1(cur, apples - cur, plates - 1);
|
|
|
}
|
|
|
return way;
|
|
|
}
|
|
|
|
|
|
// 新的尝试,最优解
|
|
|
// 苹果有apples个,盘子有plates个
|
|
|
// 返回有几种摆法
|
|
|
// 如果苹果数为0,有1种摆法:什么也不摆
|
|
|
// 如果苹果数不为0,但是盘子数为0,有0种摆法(做不到)
|
|
|
// 如果苹果数不为0,盘子数也不为0,进行如下的情况讨论:
|
|
|
// 假设苹果数为apples,盘子数为plates
|
|
|
// 可能性 1) apples < plates
|
|
|
// 这种情况下,一定有多余的盘子,这些盘子完全没用,所以砍掉
|
|
|
// 后续是f(apples, apples)
|
|
|
// 可能性 2) apples >= plates
|
|
|
// 在可能性2)下,讨论摆法,有如下两种选择
|
|
|
// 选择a) 不是所有的盘子都使用
|
|
|
// 选择b) 就是所有的盘子都使用
|
|
|
// 对于选择a),既然不是所有盘子都使用,那么后续就是f(apples, plates - 1)
|
|
|
// 意思是:既然不是所有盘子都使用,那盘子减少一个,然后继续讨论吧!
|
|
|
// 对于选择b),既然就是所有的盘子都使用,那么先把所有盘子都摆上1个苹果。
|
|
|
// 剩余苹果数 = apples - plates
|
|
|
// 然后继续讨论,剩下的这些苹果,怎么摆进plates个盘子里,
|
|
|
// 所以后续是f(apples - plates, plates)
|
|
|
public static int ways2(int apples, int plates) {
|
|
|
if (apples == 0) {
|
|
|
return 1;
|
|
|
}
|
|
|
if (plates == 0) {
|
|
|
return 0;
|
|
|
}
|
|
|
if (plates > apples) {
|
|
|
return ways2(apples, apples);
|
|
|
} else { // apples >= plates;
|
|
|
return ways2(apples, plates - 1) + ways2(apples - plates, plates);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 上面最优解尝试的记忆化搜索版本
|
|
|
public static int[][] dp = null;
|
|
|
|
|
|
public static int ways3(int apples, int plates) {
|
|
|
if (dp == null) {
|
|
|
dp = new int[11][11];
|
|
|
for (int i = 0; i <= 10; i++) {
|
|
|
Arrays.fill(dp[i], -1);
|
|
|
}
|
|
|
}
|
|
|
return process3(apples, plates, dp);
|
|
|
}
|
|
|
|
|
|
public static int process3(int apples, int plates, int[][] dp) {
|
|
|
if (dp[apples][plates] != -1) {
|
|
|
return dp[apples][plates];
|
|
|
}
|
|
|
int ans = 0;
|
|
|
if (apples == 0) {
|
|
|
ans = 1;
|
|
|
} else if (plates == 0) {
|
|
|
ans = 0;
|
|
|
} else if (plates > apples) {
|
|
|
ans = process3(apples, apples, dp);
|
|
|
} else {
|
|
|
ans = process3(apples, plates - 1, dp) + process3(apples - plates, plates, dp);
|
|
|
}
|
|
|
dp[apples][plates] = ans;
|
|
|
return ans;
|
|
|
}
|
|
|
|
|
|
} |