如何将解决方案从递归方法调用传递到调用方法? (回溯算法)

时间:2019-05-12 16:11:05

标签: java recursion backtracking

我正在尝试实现回溯算法,以平衡秤上的权重。它是用于大学的,因此有一些我必须使用的权重(0、2、7、20、70、200、700)。权重可以多次放置在秤上以匹配输入。例如:input(80)-> result(20,20,20,20)或input(16)-> result(7,7,2)。 我必须使用回溯和递归。

如果提案有误,我很难理解如何进行回溯。我只能退后一步,但是如果正确的解决方案需要退后两步,我的算法就会失败。

所以我的方法isInvalid()正在检查所有配重的总和是否高于输入值。如果是这样,它将除去最后的重量。 我想这是我的问题。对于输入(16),它会产生(7,7,2)->正确。 但是对于input(21)来说,它永远不会结束,因为它试图加20,然后再试图加7。然后它将超过21并删除7,但永远不会删除20。

/ *这是我的回溯算法* /

  public Proposal calc(Proposal proposal) {
    Proposal result;
    if(proposal.isInvalid()) return null;
    if(proposal.isSolution()) return proposal;

    for (int i : proposal.possibleNextSteps()) {
       Proposal newProposal = new Proposal(proposal.getWeight(), proposal.getCounterWeights());
       newProposal.apply(i);
       result = calc(newProposal);
       if (result != null) return result;
    }
    return null;
}

/ *这是提案类别(仅必填部分)* /

public class Proposal {
private int weight;
private ArrayList<Integer> counterWeights;
private static Integer[] weights = {0, 2, 7, 20, 70, 200};

public Proposal(int weight, ArrayList<Integer> counterWeights) {
    this.weight = weight;
    this.counterWeights = counterWeights;
    Arrays.sort(weights, Collections.reverseOrder());
}

public boolean isInvalid() {
    if(counterWeights.stream().mapToInt(i -> i.intValue()).sum() > weight) {
        counterWeights.remove(counterWeights.size()-1);
        return true;
    }
    return false;

}


public boolean isSolution() {
    return counterWeights.stream().mapToInt(value -> value).sum() == weight;
}

public Integer[] possibleNextSteps() {
    return  weights;
}

public void apply(int option) {
    this.counterWeights.add(option);
}

}

我在做什么错? 而且,这是反转我的权重数组的正确方法吗?

谢谢!

编辑: 我尝试了不同的东西。 我改变了这个:

Proposal newProposal = new Proposal(proposal.getWeight()- proposal.getSum(), new ArrayList<>());

这:

 public boolean isInvalid() {  
  return counterWeights.stream().mapToInt(value -> value).sum() > weight;
}

因此,现在如果我在调试模式下逐步进行操作,它几乎可以完成我想做的事情,但是它不会将解决方案从递归传递到以前的解决方案,因此它们不会累加最终解决方案。 因此,基本上,我将问题分解为较小的问题(一旦找到合适的权重,我将使用总权重和已经找到的解决方案之间的差值递归调用该方法)。但是,如何将解决方案传递给调用方法?

1 个答案:

答案 0 :(得分:0)

在以下实现中,解决方案是系数数组。索引i处的系数是位置i处的权重出现在溶液中的次数。

请注意,您可以有几种解决方案给出相同的总权重,此实现将它们全部解决。将其更改为仅返回找到的第一个解决方案很容易。

递归方法void solve(int weight, int n, int total)尝试为其总权重不大于目标权重的所有整数创建索引n

public class Solver {
    private final int[] weights;
    private int[] current;
    private final List<int[]> solutions = new ArrayList<>();

    public Solver(int...weights) {
        this.weights = weights;
    }

    public int[][] solve(int weight) {
        current = new int[weights.length];
        solutions.clear();
        solve(weight, 0, 0);
        return solutions.toArray(new int[solutions.size()][]);
    }

    public void printSolution(int[] solution) {
        int total = 0;
        for (int i = 0; i < solution.length; ++i) {
            for (int j = 0; j < solution[i]; ++j) {
                System.out.print(weights[i] + " ");
                total += weights[i];
            }
        }
        System.out.println(" total: " + total);
        System.out.println();
    }

    private void solve(int weight, int n, int total) {
        if (n >= current.length) {
            if (total == weight) {
                solutions.add(current.clone());
            }
        } else {
            solve(weight, n+1, total);
            while (total < weight) {
                ++current[n];
                total += weights[n];
                solve(weight, n+1, total);
            }
            current[n] = 0;
        }
    }
}