[C#] C#回溯法解决背包问题 →→→→→进入此内容的聊天室

来自 , 2019-05-03, 写在 C#, 查看 119 次.
URL http://www.code666.cn/view/d6a2be6d
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. namespace BackRack
  5. {
  6.     //要装入书包的货物节点
  7.     class BagNode
  8.     {
  9.         public int mark;//货物编号,从0开始记
  10.         public int weight;//货物重量
  11.         public int value;//货物价值
  12.         public BagNode(int m, int w, int v)
  13.         {
  14.             mark = m;
  15.             weight = w;
  16.             value = v;          
  17.         }
  18.     }
  19. //根据货物的数目,建立相应的满二叉树,如:3个货物,需要建立15个节点的二叉树,共三层(根节点所在的层记为0)
  20.     class BulidFullSubTree
  21.     {
  22.        public static int treeNodeNum = 0;//满二叉树节点总数
  23.       public int noleafNode = 0;//满二叉树出去叶子节点外所剩余的非叶子节点
  24.          public static TreeNode[] treeNode;//存储满二叉树所有节点的数组
  25.      
  26.         public BulidFullSubTree(int nodeNum)
  27.         {
  28.             treeNodeNum = Convert.ToInt32(Math.Pow(2,nodeNum+1)-1);
  29.              noleafNode = Convert.ToInt32(treeNodeNum - Math.Pow(2,nodeNum));
  30.              treeNode = new TreeNode[treeNodeNum];
  31.              for (int i = 0; i < treeNodeNum; i++)
  32.              {
  33.                  treeNode[i] = new TreeNode(i.ToString());//对二叉树的所有节点初始化
  34.              }
  35.                  for (int i = 0; i < noleafNode; i++)
  36.                  {
  37.                      //建立节点之间的关系
  38.                      treeNode[i].left = treeNode[2 * i + 1];
  39.                      treeNode[i].right = treeNode[2 * i + 2];
  40.                      treeNode[2 * i + 1].bLeftNode = true;//如果是左孩子,则记其标识变量为true
  41.                      treeNode[2 * i + 2].bLeftNode = false;
  42.                  }
  43.             treeNode[0].level=0;//约定根节点的层数为0
  44.             //根据数组下标确定节点的层数
  45.             for (int i = 1; i <= 2; i++)
  46.             {
  47.                 treeNode[i].level = 1;
  48.             }
  49.             for (int i = 3; i <= 6; i++)
  50.             {
  51.                 treeNode[i].level = 2;
  52.             }
  53.             for (int i = 7; i <= 14; i++)
  54.             {
  55.                 treeNode[i].level = 3;
  56.             }
  57.         }
  58.     }
  59. //利用回溯法寻找最优解的类
  60.     class DealBagProblem
  61.     {
  62.         public TreeNode[] treeNode = BulidFullSubTree.treeNode;//获取建立好的二叉树
  63.         int maxWeiht = 0;//背包最大承重量
  64.         int treeLevel =Convert.ToInt32(Math.Floor(Math.Log(BulidFullSubTree.treeNodeNum,2)))+1;//二叉树的最大层数
  65.         int []optionW=new int[100];//存储最优解的数组
  66.         int[] optionV = new int[100];//存储最优解的数组
  67.         int i = 0;//计数器,记录相应数组的下标
  68.         int midTw = 0;//中间变量,存储程序回溯过程中的中间值
  69.         int midTv = 0;//中间变量,存储程序回溯过程中的中间值
  70.         int midTw1 = 0;//中间变量,存储程序回溯过程中的中间值
  71.         int midTv2 = 0;//中间变量,存储程序回溯过程中的中间值
  72.         BagNode[] bagNode;//存储货物节点
  73.         string[] solution=new string[3];//程序最终所得的最优解,分别存储:最优价值,总重量,路径
  74.        // int[] bestWay=new int[100];
  75.         TraceNode[] Optiontrace=new TraceNode[100];//存储路径路径
  76.      
  77.         public DealBagProblem(BagNode[] bagN,TreeNode[] treeNode,int maxW)
  78.         {
  79.             bagNode = bagN;
  80.             maxWeiht = maxW;
  81.             for (int i = 0; i < Optiontrace.Length; i++)
  82.             {
  83.                 //将路径数组对象初始化
  84.                 Optiontrace[i] = new TraceNode();
  85.             }
  86.         }
  87.         //核心算法,进行回溯
  88.         //cursor:二叉树下一个节点的指针;tw:当前背包的重量;tv:当前背包的总价值
  89.         public void BackTrace(TreeNode cursor,int tw,int tv)
  90.         {
  91.             if(cursor!=null)//如果当前节点部位空值
  92.             {
  93.                 midTv = tv;
  94.                 midTw = tw;
  95.                 if (cursor.left != null && cursor.right != null)//如果当前节点不是叶子节点
  96.                 {
  97.                     //如果当前节点是根节点,分别处理其左右子树
  98.                     if (cursor.level == 0)
  99.                     {
  100.                         BackTrace(cursor.left, tw, tv);
  101.                         BackTrace(cursor.right, tw, tv);
  102.                     }
  103.                     //如果当前节点不是根节点
  104.                     if (cursor.level > 0)
  105.                     {
  106.                         //如果当前节点是左孩子
  107.                         if (cursor.bLeftNode)
  108.                         {
  109.                             //如果将当前货物放进书包而不会超过背包的承重量
  110.                             if (tw + bagNode[cursor.level - 1].weight <= maxWeiht)
  111.                             {
  112.                                 //记录当前节点放进书包
  113.                                 Optiontrace[i].mark = i;
  114.                                 Optiontrace[i].traceStr += "1";
  115.                                 tw = tw + bagNode[cursor.level - 1].weight;
  116.                                 tv=tv+bagNode[cursor.level - 1].value;
  117.                                 if (cursor.left != null)
  118.                                 {
  119.                                     //如果当前节点有左孩子,递归
  120.                                     BackTrace(cursor.left, tw, tv);
  121.                                 }
  122.                                 if (cursor.right != null)
  123.                                 {
  124.                                     //如果当前节点有左、右孩子,递归
  125.                                     BackTrace(cursor.right, midTw, midTv);
  126.                                 }
  127.                             }
  128.                            
  129.                         }
  130.                             //如果当前节点是其父节点的右孩子
  131.                         else
  132.                         {
  133.                             //记录当前节点下的tw,tv当递归回到该节点时,以所记录的值开始向当前节点的右子树递归
  134.                             midTv2 = midTv;
  135.                             midTw1 = midTw;
  136.                             Optiontrace[i].traceStr += "0";
  137.                             if (cursor.left != null)
  138.                             {
  139.                                 BackTrace(cursor.left, midTw, midTv);
  140.                             }
  141.                             if (cursor.right != null)
  142.                             {
  143.                                 //递归所传递的midTw1与midTv2是先前记录下来的
  144.                                 BackTrace(cursor.right, midTw1, midTv2);
  145.                             }
  146.                         }
  147.                     }
  148.                 }
  149.                 //如果是叶子节点,则表明已经产生了一个临时解
  150.                 if (cursor.left == null && cursor.right == null)
  151.                 {
  152.                     //如果叶子节点是其父节点的左孩子
  153.                     if (cursor.bLeftNode)
  154.                     {
  155.                         if (tw + bagNode[cursor.level - 1].weight <= maxWeiht)
  156.                         {
  157.                             Optiontrace[i].traceStr += "1";
  158.                             tw = tw + bagNode[cursor.level - 1].weight;
  159.                             tv = tv + bagNode[cursor.level - 1].value;
  160.                             if (cursor.left != null)
  161.                             {
  162.                                 BackTrace(cursor.left, tw, tv);
  163.                             }
  164.                             if (cursor.right != null)
  165.                             {
  166.                                 BackTrace(cursor.right, midTw, midTv);
  167.                             }
  168.                         }
  169.                     }
  170.                     //存储临时优解
  171.                     optionV[i] = tv;
  172.                     optionW[i] = tw;
  173.                     i++;
  174.                     tv = 0;
  175.                     tw = 0;
  176.                 }
  177.             }
  178.         }
  179.         //从所得到的临时解数组中找到最优解
  180.         public string[] FindBestSolution()
  181.         {
  182.             int bestValue=-1;//最大价值
  183.             int bestWeight = -1;//与最大价值对应的重量
  184.             int bestMark = -1;//最优解所对应得数组编号(由i确定)
  185.             for (int i = 0; i < optionV.Length; i++)
  186.             {
  187.                 if (optionV[i] > bestValue)
  188.                 {
  189.                     bestValue=optionV[i];
  190.                     bestMark = i;
  191.                 }
  192.             }
  193.             bestWeight=optionW[bestMark];//重量应该与最优解的数组下标对应
  194.             for (int i = 0; i < Optiontrace.Length; i++)
  195.             {
  196.                 if (Optiontrace[i].traceStr.Length == bagNode.Length&&i==bestMark)
  197.                 {
  198.                     //找到与最大价值对应得路径
  199.                     solution[2]=Optiontrace[i].traceStr;
  200.                 }
  201.             }
  202.                 solution[0] = bestWeight.ToString();
  203.             solution[1] = bestValue.ToString();
  204.             return solution;
  205.         }
  206.     }
  207. class Program
  208.     {
  209.         static void Main(string[] args)
  210.         {
  211.             //测试数据(货物)
  212.             //Node[] bagNode = new Node[100];
  213.             //BagNode bagNode1 = new BagNode(0, 5, 4);
  214.             //BagNode bagNode2 = new BagNode(1, 3, 4);
  215.             //BagNode bagNode3 = new BagNode(2, 2, 3);
  216.            
  217.             //测试数据(货物)
  218.             BagNode bagNode1 = new BagNode(0, 16, 45);
  219.             BagNode bagNode2 = new BagNode(1, 15, 25);
  220.             BagNode bagNode3 = new BagNode(2, 15, 25);
  221.        
  222.            BagNode[] bagNodeArr = new BagNode[] {bagNode1,bagNode2,bagNode3};
  223.            BulidFullSubTree bfs = new BulidFullSubTree(3);
  224.             //第3个参数为背包的承重
  225.            DealBagProblem dbp = new DealBagProblem(bagNodeArr,BulidFullSubTree.treeNode,30);
  226.      
  227.             //找到最优解并将其格式化输出
  228.            dbp.BackTrace(BulidFullSubTree.treeNode[0],0,0);
  229.            string[] reslut=dbp.FindBestSolution();
  230.          
  231.            if (reslut[2] != null)
  232.            {
  233.                Console.WriteLine("该背包最优情况下的货物的重量为:{0}\n            货物的最大总价值为:{1}", reslut[0].ToString(), reslut[1].ToString());
  234.                Console.WriteLine("\n");
  235.                Console.WriteLine("该最优解的货物选择方式为:{0}", reslut[2].ToString());
  236.                char[] r = reslut[2].ToString().ToCharArray();
  237.                Console.WriteLine("被选择的货物有:");
  238.                for (int i = 0; i < bagNodeArr.Length; i++)
  239.                {
  240.                    if (r[i].ToString() == "1")
  241.                    {
  242.                        Console.WriteLine("货物编号:{0},货物重量:{1},货物价值:{2}", bagNodeArr[i].mark, bagNodeArr[i].weight, bagNodeArr[i].value);
  243.                    }
  244.                }
  245.            }
  246.            else
  247.            {
  248.                Console.WriteLine("程序没有找到最优解,请检查你输入的数据是否合适!");
  249.            }
  250.         }
  251.     }
  252. //存储选择回溯路径的节点
  253. public class TraceNode
  254.     {
  255.       public int mark;//路径编号
  256.       public string traceStr;//所走过的路径(1代表取,2代表舍)
  257.         public TraceNode(int m,string t)
  258.         {
  259.             mark = m;
  260.             traceStr = t;
  261.         }
  262.         public TraceNode()
  263.         {
  264.             mark = -1;
  265.             traceStr = "";
  266.         }
  267.     }
  268. //回溯所要依附的满二叉树
  269.     class TreeNode
  270.     {
  271.         public TreeNode left;//左孩子指针
  272.         public TreeNode right;//右孩子指针
  273.         public int level;//数的层,层数代表货物的标识
  274.         string symb;//节点的标识,用其所在数组中的下标,如:“1”,“2”
  275.         public bool bLeftNode;//当前节点是否是父节点的左孩子
  276.         public TreeNode(TreeNode l, TreeNode r, int lev,string sb,bool ln)
  277.         {
  278.             left = l;
  279.             right = r;
  280.             level = lev;
  281.             symb = sb;
  282.             bLeftNode = ln;
  283.         }
  284.         public TreeNode(string sb)
  285.         {
  286.             symb = sb;
  287.         }
  288.     }
  289.  
  290. }
  291. //csharp/7207

回复 "C#回溯法解决背包问题"

这儿你可以回复上面这条便签

captcha