Вот предложение решения.
Сначала я решил первую проблему: я полагаю, что все числа и цель являются положительным числом, тогда я решаю реальную проблему.
Для этого я в основном разлагаю проблему в подзадачах.
Проиллюстрируем на примере:
Numbers: 1,3,8,2,7-мишени: 10
Первый: сортировать список: Numbers: 8,7,3,2,1 Цель: 10 Затем найти рекурсивно решения следующих проблем:
Numbers: 7,3,2,1 цели: 10-8 = 2
Число: 3,2,1 цели: 10-7 = 3
номера: 2,1-мишени: 10-3 = 2
Числа: 1 мишень: 10-1 = 9
Целью размещения больших чисел перед, чтобы быстро устранить решения, включая этот номер (поскольку сумма быстро превышает целевую).
Вот прокомментировал код для решения этой подпрограммы задачи:
import java.util.ArrayList;
import java.util.List;
public class Problem {
/*
* Used at the end to recompose the solutions.
* This value is null for the root problem.
*/
private Integer nodeValue;
//The POSITIVE target sum
private int target;
//List of POSITIVE numbers, supposed to be sorted
private List<Integer> numbers;
private List<Problem> listSubProblems;
/*
* Link to the parent problem.
* Useful at the end to generate the results.
*/
private Problem parentProblem;
public Problem(int target, List<Integer> numbers, Integer nodeValue, Problem parentProblem){
this.target=target;
this.numbers=numbers;
this.nodeValue=nodeValue;
this.parentProblem=parentProblem;
this.listSubProblems =new ArrayList<Problem>();
}
public void solve(){
buildSubProblems();
for(Problem problem : listSubProblems){
problem.solve();
}
}
/**
* Builds a List of sub problems.
* For example, if {@link #numbers} contains 9 8 5 3, with target 10
* this method will return the following sub problems:
*
* <table>
* <tr>
* <td>nodeValue</td><td>target</td><td>numbers</td>
* </tr>
* <tr>
* <td>9</td><td>10-9=1</td><numbers>8 5 3</numbers>
* </tr>
* <tr>
* <td>8</td><td>10-8=2</td><numbers>5 3</numbers>
* </tr>
* <tr>
* <td>5</td><td>10-5=5</td><numbers>3</numbers>
* </tr>
*
* </table>
*
*/
private void buildSubProblems(){
int numbersSize=numbers.size();
/*
* Numbers are supposed to be positive so if the target is negative,
* there is no chance to find a valid solution.
* As the list of numbers is sorted, the case when target < 0 happens quickly
* Hence, it quickly removes combinations implying big numbers
*/
if(target>=0 && numbersSize> 1){
for(int i=0;i<numbersSize;i++){
Integer nodeValue=numbers.get(i);
List<Integer> subList=numbers.subList(i+1,numbersSize);
int newTarget=this.target-nodeValue;
Problem problem=new Problem(newTarget, subList, nodeValue, this);
System.out.println("Created problem: "+problem.dump());
this.listSubProblems.add(problem);
}
}
}
/**
* @return True is the Problem contains exactly one number and that number equals the target.
*/
public boolean isNodeSolution(){
return this.target==0;
}
public Integer getNodeValue(){
return this.nodeValue;
}
public List<Problem> getListSubProblems(){
return this.listSubProblems;
}
public Problem getParentProblem(){
return this.parentProblem;
}
public String dump(){
StringBuilder sb=new StringBuilder();
sb.append("{nodeValue: "+this.nodeValue);
sb.append("; target: "+target);
sb.append("; numbers:");
for(Integer integer : numbers){
sb.append(integer+",");
}
sb.append("}");
sb.append("Valid? : "+ isNodeSolution());
return sb.toString();
}
}
Вот код, который показывает, как проверить:
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class Main {
public static void main(String[] args) throws Exception{
Integer numbers[]={1,3,8,2,7};
int target=10;
List<Integer> listNumbers= Arrays.asList(numbers);
Collections.sort(listNumbers);
Collections.reverse(listNumbers);
//Build the root problem
Problem problem=new Problem(target,listNumbers,null,null);
//Solve it
problem.solve();
//Dump the result.
dumpResult(problem);
System.out.println("Done!");
}
private static void dumpResult(Problem problem){
for(Problem p:problem.getListSubProblems()){
if(p.isNodeSolution()){
System.out.print("\nSolution :");
dumpSolution(p);
}
dumpResult(p);
}
}
private static void dumpSolution(Problem problem){
//If the current node is not the root problem
if(problem.getParentProblem()!=null){
System.out.print(problem.getNodeValue() + ", ");
dumpSolution(problem.getParentProblem());
}
}
}
А вот пример вывода :
Created problem: {nodeValue: 8; target: 2; numbers:7,3,2,1,}Valid? : false
Created problem: {nodeValue: 7; target: 3; numbers:3,2,1,}Valid? : false
Created problem: {nodeValue: 3; target: 7; numbers:2,1,}Valid? : false
Created problem: {nodeValue: 2; target: 8; numbers:1,}Valid? : false
Created problem: {nodeValue: 1; target: 9; numbers:}Valid? : false
Created problem: {nodeValue: 7; target: -5; numbers:3,2,1,}Valid? : false
Created problem: {nodeValue: 3; target: -1; numbers:2,1,}Valid? : false
Created problem: {nodeValue: 2; target: 0; numbers:1,}Valid? : true
Created problem: {nodeValue: 1; target: 1; numbers:}Valid? : false
Created problem: {nodeValue: 3; target: 0; numbers:2,1,}Valid? : true
Created problem: {nodeValue: 2; target: 1; numbers:1,}Valid? : false
Created problem: {nodeValue: 1; target: 2; numbers:}Valid? : false
Created problem: {nodeValue: 2; target: -2; numbers:1,}Valid? : false
Created problem: {nodeValue: 1; target: -1; numbers:}Valid? : false
Created problem: {nodeValue: 2; target: 5; numbers:1,}Valid? : false
Created problem: {nodeValue: 1; target: 6; numbers:}Valid? : false
Solution :2, 8,
Solution :3, 7, Done!
Теперь это не касается первоначальной проблемы, которая подразумевает отрицательные числа. Чтобы решить этот случай, выделите все отрицательные числа и вычислите все комбинации отрицательных чисел, с суммой.
Тогда для каждой суммы отрицательного числа, создать вложенную задачу, содержащую только положительные числа и соответствующую цель (начальная цель - сумма отрицательных чисел)
Один из способов улучшить его: Сложность задач зависит по числу комбинаций отрицательных чисел. Таким образом, если число отрицательных чисел больше, чем положительное, вы можете просто инвертировать все значения и решить проблему инверсии.
Еще один способ улучшить его: вы можете сохранить в памяти сумму положительных чисел каждой проблемы. Если сумма + nodeValue < целевая, то бесполезно продолжать исследовать ветку.
что-то заставляет меня хотеть думать, что это трудная проблема np, но я не знаю, что –
Жаль, что это больше не помечено C#. Я собирался предложить 'var result = из подмножества в input.PowerSet(), где subset.Sum() == 0 select подмножество;' – dtb
имеет конечное решение с конечным вводом ... но это я думаю O (n!^3) - или что-то столь же уродливое. – Randy