题目链接:https://www.dotcpp.com/oj/problem1576.html

    1. import java.util.*;
    2. public class Test {
    3. static int N; //允许粘贴的邮票数量
    4. static int K; //邮票种类数量(不同面值的邮票数量)
    5. static int cnt = 0; //题目中的MAX
    6. static int[] arr = new int[11]; //存储当前的邮票面值方案
    7. static int[] ans = new int[11]; //存储取得 cnt 时的邮票面值方案(即最佳方案)
    8. public static int updateMAX(int count){
    9. if(count == 0){
    10. return 0;
    11. }
    12. // 根据题目,我们要解决的原始问题是:
    13. // 给定N个邮票的位置,K种邮票种类,其邮票面值方案为:arr[1] ~ arr[count],
    14. // 如何找到最大连续数
    15. // 例如N = 3, K = 2, 且 arr[1]=1,arr[2]=3(即邮票面值分别为1,3)
    16. // 如何组合出最大连续数
    17. // 1 <-- 1
    18. // 2 <-- 1 + 1
    19. // 3 <-- 1 + 1 + 1 或 3
    20. // 4 <-- 1 + 3
    21. // 5 <-- 1 + 1 + 3
    22. // 6 <-- 3 + 3
    23. // 7 <-- 1 + 3 + 3
    24. // 8 不满足了,因为受限于 N=3, 3+3+3-->9 而 3+3+1-->7,组合不了8的情况
    25. // 因此最大连续数为7
    26. // 在这里,我们并不是直接处理上述问题,而是将其转换为
    27. // 完全背包问题求解(单张邮票的数量不限制)
    28. // 给定邮票面值方案arr[1] ~ arr[count],且背包的最大容量为N * arr[count]
    29. // (因为arr[i]为单调递增数组)
    30. // 定义dp[i]:组合成 最大连续数i 所需的最小邮票数量
    31. // 状态转移方程: dp[j] = min(dp[j], dp[j-arr[i]]+1);
    32. // 含义:如果选择邮票arr[i]去组合 最大连续数j,则相应的背包容量要减少(即j-arr[i]),且邮票使用数量加1
    33. // 如果不选邮票arr[i]去组合 最大连续数j,则背包容量、邮票使用数量均不变,二者取最小
    34. // 那怎么去求解我们需要的最大连续数呢?
    35. // dp[N * arr[count]] 仅仅代表着最大连续数 N * arr[count]所需的最小邮票数量,并没有保证这个最小邮票数量 <= N
    36. // 因此,我们需要遍历dp数组,一遇到 dp[i] > N的时候就停止遍历,i-1即为我们所需的最大连续数
    37. int[] dp = new int[1010];
    38. //dp[i]:组合出 最大连续数i 所需的最小邮票数量
    39. //初始化dp
    40. Arrays.fill(dp, Integer.MAX_VALUE);
    41. dp[0] = 0;
    42. for(int i = 1; i <= count; i++){
    43. for (int j = arr[i]; j <= N * arr[count]; j++){
    44. dp[j] = Math.min(dp[j], dp[j - arr[i]]+1);
    45. }
    46. }
    47. int i = 1;
    48. for(; i <= N * arr[count]; i++){
    49. if (dp[i] > N){
    50. break;
    51. }
    52. }
    53. return i - 1;
    54. }
    55. public static void dfs(int count, int maxSeqNum){
    56. if(count > K){ //说明邮票面值的取值个数为K
    57. if(maxSeqNum > cnt){
    58. //maxSeqNum:由当前邮票面值(前count个邮票)构成的最大连续数(即题目中的MAX)
    59. //只有maxSeqNum > cnt,需要更新答案
    60. cnt = maxSeqNum;
    61. for(int i = 1; i <= K; i++){
    62. ans[i] = arr[i];
    63. }
    64. }
    65. return;
    66. }
    67. //根据前 count-1 个邮票面值的情况,枚举第 count 个邮票面值的取值
    68. for(int i = arr[count-1] + 1; i <= maxSeqNum + 1; i++){
    69. //将当前的取值作为第count个邮票面值,然后查看是否有更优的最大连续数
    70. arr[count] = i;
    71. //递归到下一层,确定第count+1个邮票面值
    72. //利用updateMAX()更新前count个邮票面值组合得到的最大连续数
    73. dfs(count+1, updateMAX(count));
    74. }
    75. }
    76. public static void main(String[] args) {
    77. Scanner sc = new Scanner(System.in);
    78. N = sc.nextInt();
    79. K = sc.nextInt();
    80. dfs(1, 0);
    81. for(int i = 1; i <= K; i++){
    82. System.out.printf(ans[i] + " ");
    83. }
    84. System.out.println();
    85. System.out.println("MAX=" + cnt);
    86. }
    87. }