算法题: 求一个整数数组中,通过元素加减运算得到指定结果的所有运算过程. 例如【5,4,6,7,1】= 9 ?

题目: 给定一个整数数组int[] a (a.length > 1),和一个整数值 m,试输出所有运算结果等于m的运算过程。可使用的运算方式只有加法和减法。数组元素最多参与一次运算。例如,给定数组【5,4,6,7,1】和整数9,输出运算结果为9的运算过程如下:

+5+4=9
+5+4+6-7+1=9
+5+4-6+7-1=9
+5-4+7+1=9
+4+6-1=9
-4+6+7=9
-5+6+7+1=9


这个题目,我们可以使用回溯算法得到所有的解。回溯法在问题的解空间树中,按深度优先策略,从根节点出发搜索解空间树。算法搜索至解空间树中的任一节点时,先判断该节点是否包含问题的解。如果不包含,则跳过对已该结点为根的子数的搜索,逐层向其祖先结点回溯。否则,进入该子树,继续按深度优先策略搜索。回溯法求问题所有解时,要回溯到根,且根结点的所有子树都已被搜索遍才结束。回溯法求问题的一个解时,只要搜索到问题的一个解就可结束。

回溯法通常包含3个步骤

  • 针对所给问题,定义问题的解空间
  • 确定易于搜索的解空间结构。常见的结构一般为n叉树结构,而且一般都是满n叉树。
  • 以深度优先方式搜索解空间,并在搜索过程中使用剪枝函数避免无效搜索。深度优先策略可以选择先序遍历,中序遍历,和后序遍历。

对于给定的这个题目,我们首先要确定问题的解空间。由于如下的条件限定

  • 运算过程只能使用加法和减法
  • 数组元素最多参与一次运算

我们可以把数组元素的操作转换为 (x1 * +1 ) + (x2 * -1) + (x3 * 0) ....... = ? ,以题目为例, 容易看出题目需要的解向量为 { (1,1,1,-1,1), (-1,0,1,1,1)..... } ,然后我们可以确定出解空间结构为一个3叉树,而且是一个满三叉树。三叉树深度是给定数组的长度加一,如题中数组长度为5,那么解空间结构的三叉树的深度为6。由于篇幅限制,这里只画了最左部分节点的结构。


最后,剩下的步骤就是遍历这颗三叉树,检查每个节点的结果是否符合要求。我们以根节点,左子树,中子树,和右子树的顺序进行深度优先遍历。那么以最左边树为例,其遍历的结果如上图所示,其中只有遍历到第三层时的加法运算组合满足要求 (5+4 = 9),那么我们可以得到一个解向量,即 { (1,1,0,0,0) }。另外,符合要求的解,很有可能在叶子结点获得。例如(5+4+6-7+1=9),对应的解向量为{ (1,1,1,-1,1 ) }。

代码如下

解空间数据结构的节点类

[java]  view plain  copy
  1. package com.csdn.blog.TechNerd.TraceBack;  
  2. /* 
  3.  * 构造树节点,包含左子节点,中子节点,和右子节点的引用。以及该节点深度及数据信息。 
  4.  */  
  5. public class Node {  
  6.       
  7.     private Node _lnode;  
  8.     private Node _rnode;  
  9.     private Node _mnode;  
  10.     private int _data;  
  11.       
  12.     private int _depth;  
  13.       
  14.     public Node(int data,int depth){  
  15.         this._data = data;  
  16.         this._depth = depth;  
  17.     }  
  18.       
  19.       
  20.     public void setLNode(Node lnode){  
  21.         this._lnode = lnode;  
  22.     }  
  23.   
  24.     public void setMNode(Node mnode){  
  25.         this._mnode = mnode;  
  26.     }  
  27.       
  28.     public void setRNode(Node rnode){  
  29.         this._rnode = rnode;  
  30.     }  
  31.       
  32.   
  33.       
  34.     public int getData(){  
  35.         return this._data;  
  36.     }  
  37.       
  38.     public int getDepth(){  
  39.         return this._depth;  
  40.     }  
  41.       
  42.     public Node getLNode(){  
  43.         return this._lnode;  
  44.     }  
  45.       
  46.     public Node getMNode(){  
  47.         return this._mnode;  
  48.     }  
  49.       
  50.     public Node getRNode(){  
  51.         return this._rnode;  
  52.     }  
  53.       
  54.   
  55.       
  56. }  

如下类为解空间数据结构,包含回溯算法的应用。

[java]  view plain  copy
  1. package com.csdn.blog.TechNerd.TraceBack;  
  2.   
  3.   
  4. public class TraceBackTree {  
  5.       
  6.     private Node _root;  
  7.     private int _depth;  
  8.       
  9.       
  10.     private int[] _a;    
  11.     private int _m;     
  12.       
  13.       
  14.     public TraceBackTree(Node root,int depth,int[] a,int m){  
  15.         this._root = root;  
  16.         this._depth = depth;  
  17.         buildBTree();  
  18.         this._a = a;  
  19.         this._m = m;  
  20.     }  
  21.       
  22.    /* 
  23.     * 构建解空间数据结构,题目所需要的是一个满三叉树。 
  24.     */  
  25.     private void buildBTree(){  
  26.           
  27.         this._root.setLNode(createNode(1,2));  
  28.         this._root.setMNode(createNode(0,2));  
  29.         this._root.setRNode(createNode(-1,2));  
  30.           
  31.     }  
  32.       
  33.     private Node createNode(int data,int depth){  
  34.           
  35.         if (depth <= this._depth){  
  36.             Node n = new Node(data,depth);  
  37.             n.setLNode(createNode(1,depth + 1));  
  38.             n.setMNode(createNode(0,depth + 1));  
  39.             n.setRNode(createNode(-1,depth +1));  
  40.             return n;  
  41.         }else{  
  42.             return null;  
  43.         }  
  44.     }  
  45.       
  46.     /* 
  47.      * 按照根节点,左子节点,中子节点,右子节点的顺序对数进行遍历,打印所有节点。 
  48.      */  
  49.     public void preOrderTraverse(){  
  50.         preOrderTraverse(this._root);  
  51.     }  
  52.   
  53.     private void preOrderTraverse(Node n){  
  54.         if (n != null){  
  55.             printNode(n);  
  56.             preOrderTraverse(n.getLNode());  
  57.             preOrderTraverse(n.getMNode());  
  58.             preOrderTraverse(n.getRNode());  
  59.         }  
  60.     }  
  61.       
  62.     private void printNode(Node n){  
  63.         System.out.print(n.getData() + " ");  
  64.           
  65.     }  
  66.     /* 
  67.      *回溯法求所有解。  
  68.      */  
  69.     public void backTrace(int[] a,int m){  
  70.   
  71.         int[] x = new int[this._depth - 1]; //定义存储解向量的数组。该数组长度与题目给定的数组长度相等。  
  72.         backTrace(this._root,x);  
  73.           
  74.     }  
  75.     private void backTrace(Node n,int[] x){  
  76.   
  77.             if (n.getDepth() > 1) x[n.getDepth() - 2] = n.getData(); //将节点值付给解向量数组。  
  78.               
  79.             if (constraints(x,n.getDepth() - 2)){   
  80.                 printSolution(x,n.getDepth() - 2);  
  81.             }  
  82.             if (n.getLNode() != null)   
  83.                 backTrace(n.getLNode(),x);  
  84.             if (n.getMNode() != null)   
  85.                 backTrace(n.getMNode(),x);  
  86.             if (n.getRNode() != null)   
  87.                 backTrace(n.getRNode(),x);  
  88.               
  89.     }  
  90. /* 
  91.  * 检查目前解向量是否满足题目要求,就和等于指定值。 
  92.  */      
  93. private boolean constraints(int[] x,int boundary) {  
  94.         int sum = 0;  
  95.         for (int i=0;i<= boundary;i++){  
  96.             sum += _a[i] * x[i];   
  97.         }  
  98.         return (sum == _m && x[boundary] != 0);  
  99.     }  
  100.   
  101.   
  102.     private void printSolution(int[] x,int boundary) {  
  103.         for (int i =0;i<= boundary;i++){  
  104.             if (x[i] == 1){  
  105.                 System.out.print("+"+ _a[i]);  
  106.             }else if (x[i] == 0){  
  107.                   
  108.             }else if (x[i] == -1){  
  109.                 System.out.print("-" + _a[i]);  
  110.             }  
  111.         }  
  112.           
  113.         System.out.println("=" + this._m);  
  114.           
  115.           
  116.     }  
  117.       
  118.     public static void main(String[] args){  
  119.         int[] a = {5,4,6,7,1};  
  120.         int m = 9;  
  121.         //创建的数的深度为给定数组的长度加一   
  122.         TraceBackTree bt = new TraceBackTree(new Node(1,1),a.length + 1,a,m);    
  123.         //按照根节点,左子节点,中子节点,右子节点的顺序对数进行遍历,打印所有节点。  
  124.         // bt.preOrderTraverse();    
  125.           
  126.         bt.backTrace(a,m);  
  127.     }  
  128. }  
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值