题目
测试用例
解析
本题在笔者烦躁的情况下逼着自己去做的,第一次做的时候效果不是很好,想的是动态规划的方法,最终未作出,去睡觉了。早上想着继续做,感觉回溯可行。尝试一下,果真如此,继而又把动态规划的方法写了出来。
回溯
如果告诉你一个数n,分成3份,有多少种方法,你会怎么做?
本着先把题目做出来的想法,笔者会设置i,j,k三个循环变量,j>=i,k>=j。以此保证不会出现1 1 5 与 5 1 1 或1 5 1 重复的情况出现。
想到这里,我们可以看到与回溯的思路很像,做出选择–>下一层–>撤销选择,来代替循环。
选择列表是对于i来说是[1~n/3],对于j来说是[1~n/2],对于k来说是[1~n],那么我们可以设置三个传递的参数。来记录不同的选择范围。
public static void backtrack(int i,int n,int k){
if (n==1 && k>1){
return;
}
if (k==1){
count++;
return;
}
for (int j=i;j<=n/k;j++){
backtrack(j,n-j,k-1);
}
}
完整代码
import java.util.Scanner;
public class ALGO_22 {
public static int count = 0;
//数的划分
//将整数n分成k份,且每份不能为空,任意两份不能相同(不考虑顺序)。
// 例如:n=7,k=3,下面三种分法被认为是相同的。
// 1,1,5; 1,5,1; 5,1,1;
// 问有多少种不同的分法。
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int k = scanner.nextInt();
backtrack(1,n,k);
System.out.println(count);
}
//使用的回溯
public static void backtrack(int i,int n,int k){
if (n==1 && k>1){
return;
}
if (k==1){
count++;
return;
}
for (int j=i;j<=n/k;j++){
backtrack(j,n-j,k-1);
}
}
}
动态规划
对于将7划分为3份,我们可以把问题等价于如下:
将1划分为2份,余下6 的方法加上
将2划分为2份,余下5 的方法加上
将3划分为2份,余下4 的方法的方法加上
将4划分为2份,余下3
为什么最后一个不行呢?因为1 1 5 和5 1 1 看作是同一个,所以我们的三个数字选用递增的方式。那么就不能到4。而这个4是怎么求出来的呢?想一下,7如果均匀分配的话,那么平均每个为7/3。我们的第一个数是不可能大于7/3的。那么就可以把界限设定在7/3。给的是n和k,那么界限就是n/k。
- base case是当数分为1份时,只能有1种方法。
完整代码
import java.util.Scanner;
public class ALGO_22b {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int k = scanner.nextInt();
int[][] dp = new int[n+1][k+1];
//dp[i][j] :整数i,分成j份,有dp[i][j]种方法
//base case 整数i分为1份,只有一种分法
for (int i=1;i<=n;i++){
dp[i][1] = 1;
}
//dp转移
for (int i=2;i<=n;i++){
for (int j=2;j<=k;j++){
for (int m=1;m<=n/k;m++){
dp[i][j] += dp[m][j-1];
}
}
}
System.out.println(dp[n][k]);
}
}