递归回溯的问题

时间:2011-04-21 01:07:03

标签: java recursion backtracking

嘿伙计们,最近发布了我的算法问题。

Finding the numbers from a set which give the minimum amount of waste

我略微修改了代码,所以它现在回溯到一定程度,但输出仍然存在缺陷。我调试了这个相当大的检查所有变量值,似乎无法找出问题。

与直接解决方案相反,建议再次提供了很大的帮助。我认为我的代码只有几个问题,但我无法解决问题。

//来自上一篇文章:

基本上,一个集合被传递给下面的这个方法,并且还传入了一个条形的长度。解决方案应该输出集合中的数字,如果集合中的某些数字从中移除,则会产生最少量的浪费。酒吧长度。因此,条形长度10,设置包括6,1,4,因此解决方案是6和4,并且浪费是0.我在通过集合回溯的条件有一些麻烦。我也尝试使用浪费的“全局”变量来帮助回溯方面,但无济于事。

SetInt是一个手动制作的集合实现,它可以添加,删除,检查集合是否为空并从集合中返回最小值。

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package recursivebacktracking;

/**
 *
 * @author User
 */
public class RecBack {

        int WASTAGE = 10;
        int BESTWASTAGE;
        int BARLENGTH = 10;

    public void work()
    {


         int[] nums = {6,1,2,5};
        //Order Numbers
        SetInt ORDERS = new SetInt(nums.length);
        SetInt BESTSET = new SetInt(nums.length);
        SetInt SOLUTION = new SetInt(nums.length);

        //Set Declarration


        for (int item : nums)ORDERS.add(item);
        //Populate Set

        SetInt result = tryCutting(ORDERS, SOLUTION, BARLENGTH, WASTAGE);
        result.printNumbers();


    }
    public SetInt tryCutting(SetInt possibleOrders, SetInt solution, int lengthleft, int waste)
      {


        for (int i = 0; i < possibleOrders.numberInSet(); i++) // the repeat
          {


            int a = possibleOrders.min(); //select next candidate
              System.out.println(a);

            if (a <= lengthleft) //if accecptable
              { 
                solution.add(a); //record candidate
                lengthleft -= a;
                WASTAGE = lengthleft;
                possibleOrders.remove(a); //remove from original set



                if (!possibleOrders.isEmpty()) //solution not complete
                  {
                      System.out.println("this time");
                    tryCutting(possibleOrders, solution, lengthleft, waste);//try recursive call
                    BESTWASTAGE = WASTAGE;
                    if ( BESTWASTAGE <= WASTAGE )//if not successfull
                      {
                        lengthleft += a;
                        solution.remove(a);

                          System.out.println("never happens");
                      }

                  } //solution not complete
              }

          } //for loop

        return solution;

      }




}

1 个答案:

答案 0 :(得分:1)

您是否考虑使用位掩码算法而不是使用回溯?我认为它会使你的算法更简单。

以下是您将如何执行此操作的大纲:

设N是集合中的元素数。因此,如果集合为{6,1,2,5},那么N将为4.让max_waste成为我们可以消除的最大浪费(在您的示例中为10)。

int best = 0;  // the best result so far

for (int mask = 1; mask <= (1<<N)-1; ++mask) {

    // loop over each bit in the mask to see if it's set and add to the sum
    int sm = 0;
    for (int j = 0; j < N; ++j) {
        if ( ((1<<j)&mask) != 0) {
            // the bit is set, add this amount to the total
            sm += your_set[j];

            // possible optimization: if sm is greater than max waste, then break
            // out of loop since there's no need to continue
        }
    }

    // if sm <= max_waste, then see if this result produces a better one
    // that our current best, and store accordingly
    if (sm <= max_waste) {
        best = max(max_waste - sm);
    }
}

此算法与回溯非常相似,并且具有相似的复杂性,它只是不使用递归。

位掩码基本上是二进制表示,其中1表示我们使用集合中的项目,0表示我们不使用。由于我们从1循环到(1<<N)-1,我们正在考虑给定项目的所有可能子集。

请注意,随着N变大,此算法的运行时间会非常快地增加,但如果N <=约20,则应该没问题。顺便提一下,回溯也适用同样的限制。如果您需要更快的性能,则需要考虑另一种技术,如动态编程。

对于回溯,您只需要跟踪您所在的集合中的哪个元素,并尝试使用该元素或不使用它。如果您使用它,则将其添加到总数中,如果不是,则在不增加总数的情况下进行下一次递归调用。然后,你递减总数(如果你增加它),这是回溯进入的地方。

它与上面的位掩码方法非常相似,我提供了位掩码解决方案,以帮助您更好地理解回溯算法的工作原理。

修改的 好的,我没有意识到你被要求使用递归。

HINT1 的 首先,我认为只需使用一个递归函数并将逻辑放在该函数中,就可以大大简化代码。没有必要提前构建所有的集合然后处理它们(我不完全确定你正在做什么,但它似乎从你的代码开始)。您可以构建集合,然后跟踪您在集合中的位置。当你到达集合的末尾时,看看你的结果是否更好。

提示二的 如果您仍需要更多提示,请尝试考虑您的回溯功能应该做什么。什么是终止条件?当我们达到终止条件时,我们需要记录什么(例如,我们是否获得了新的最佳结果等)?

Hint3 扰流警报 下面是一个C ++实现,为您提供一些想法,所以如果您想自己更多地处理它,请在此处阅读。

int bestDiff = 999999999;
int N;
vector< int > cur_items;
int cur_tot = 0;
int items[] = {6,1,2,5};
vector< int > best_items;
int max_waste;

void go(int at) {
  if (cur_tot > max_waste)
    // we've exceeded max_waste, so no need to continue
    return;
  if (at == N) {
    // we're at the end of the input, see if we got a better result and
    // if so, record it
    if (max_waste - cur_tot < bestDiff) {
      bestDiff = max_waste - cur_tot;
      best_items = cur_items;
    }
    return;
  }

  // use this item  
  cur_items.push_back(items[at]);
  cur_tot += items[at];
  go(at+1);

  // here's the backtracking part
  cur_tot -= items[at];
  cur_items.pop_back();

  // don't use this item
  go(at+1);
}

int main() {
  // 4 items in the set, so N is 4
  N=4;

  // maximum waste we can eliminiate is 10
  max_waste = 10;

  // call the backtracking algo
  go(0);

  // output the results
  cout<<"bestDiff = "<<bestDiff<<endl;
  cout<<"The items are:"<<endl;
  for (int i = 0; i < best_items.size(); ++i) {
    cout<<best_items[i]<<" ";
  }
  return 0;
}