• 用回溯法解决子集和问题【C#版本】


    最近在开发某项目,遇到这样一个需求:在一个账单记录的Table中,记录着每张账单及其金额,要求用户输入一个金额,从表中取出金额组合为该金额的账单【可能有很多个解,但只需要提供一例】。

    这题目看起来很简单,只是把数加起来判断,但仔细一想,难度不小。因为组合的个数没有确定,可以直接找到,即1个,或者2个,3个……N个组成,那么原先想要使用的for循环就无法使用了,因为不知道要嵌套多少层,而且跟后来的方法相比效率较低。

    后来在网上查了好久,查到了这原来这叫做子集和问题,是什么0-1背包之类的问题【数据结构学得一般T^T】需要用递归或回溯解决。因为我只需要求出一解,所以我选择了回溯法。递归将显示所有组合,但我并不需要。下面是仿照别人的思路写出的代码,我将一一注释。

     1 static void Main(string[] args)
     2 {
     3   int[] test = { 3, 7, 18, 33, 22, 6, 10, 21, 2, 15 };//测试用的数组
     4   bool[] flag = { false, false, false, false, false, false, false, false, false, false };//标志数组,与测试数组对应,true代表该数在组合中,false则不在
     5   Bubble(test);//冒泡,其实不排序也可以
     6   int target = Convert.ToInt32(Console.Read());
     7   bool result = BackTrace(test, flag, target);
     8 
     9   if (result == true)
    10   {
    11     for (int i = 0; i <= test.Length - 1; i++)
    12     {
    13     if (flag[i] == true)
    14     Console.Write("{0}, ", test[i]);
    15     }
    16   }
    17   else
    18   {
    19     Console.Write("no sub sets found!");
    20   }
    21   Console.WriteLine();
    22 }
    23 
    24   static bool BackTrace(int[] a, bool[] flag, int target)
    25   {
    26     int sum = 0;//初始化和
    27     int index = 0;//初始化索引
    28     while (index >= 0)//从第一个开始找,循环找,与for相比循环更多次
    29     {
    30       if (flag[index] == false)//如果不在组合中,则尝试把它加入
    31       {
    32       sum += a[index];
    33       flag[index] = true;
    34 
    35       if (sum == target)//如果加入后组合数与目标数一致,则说明找到组合,如果组合数大于目标数,则将刚才的元素从组合中剔除,如果小于,则什么也不做.同时继续检验下一元素.
    36       return true;
    37       else if (sum > target)
    38         {
    39         sum -= a[index];
    40         flag[index] = false;
    41         }
    42       index++;//继续检验下一元素
    43       }
    44 
    45     //如果索引到了最后,还没有找到合适的组合,那么将回溯.一般来说会出现*100011或*1000的情况,即此时flag中的元素应该在某个1之后有若干个0或01组合【先0后1】, [I个数] 1 [J个0][K个1]这样的情况,回溯到1的位置,将其变为0,然后继续往下循环检验.从后面回溯的时候,将1变为0,遇0不变.如果回溯到首位,则说明没有合适的组合存在.
    46 
    47     if (index >= a.Length)
    48     {
    49       while (flag[index - 1] == true)//如果在组合中,则退出,并往前回溯
    50       {
    51         flag[index - 1] = false;
    52         index--;
    53         sum -= a[index];
    54         if (index < 1)//此时index最大为0,但下次循环将出界,已经回溯到最开始了
    55                return false;
    56       }
    57 
    58       while (flag[index - 1] == false)//如果不在组合中,往前回溯
    59       {
    60         index--;
    61         if (index < 1)
    62           return false;//此时index最大为0,但下次循环将出界,已经回溯到最开始了
    63       }
    64       flag[index - 1] = false;
    65       sum -= a[index - 1];
    66     }
    67 
    68   }
    69   return false;
    70 
    71   }
    72 
    73   static void Bubble(int[] a)
    74   {
    75     for (int i = 0; i <= a.Length - 2; i++)
    76     for (int j = i + 1; j <= a.Length - 1; j++)
    77     {
    78       if (a[i] > a[j])
    79       {
    80         int temp = a[i];
    81         a[i] = a[j];
    82         a[j] = temp;
    83       }
    84   }
    85 
    86   foreach (int k in a)
    87   Console.Write("{0}, ", k);
    88   Console.WriteLine();
    89 }

    还应注意Console.ReadLine(),读入的是字符串,要经过Convert.ToInt32()的处理,因为输入的是字符串,其值与字面值不一致.

    此外应该还可以再优化,但过几个钟还要上班,还是睡觉去吧.

    PS:我不是通宵工作,我从10点睡到4点半,睡不着就起床写一下博客。程序员要记得劳逸结合哦!

  • 相关阅读:
    npm 版本不支持node.js的解决方法
    kolla-ansible运维
    Openstack Train部署 (kolla-ansible)
    存储使用的光纤交换机
    Openstack Train部署 (openstack-ansible)
    使用cockpit-ceph-deploy部署ceph集群
    ceph集群维护
    ceph生产环境规划
    分布式存储ceph部署
    openvswitch网桥的连接方式
  • 原文地址:https://www.cnblogs.com/kingsleylam/p/2747354.html
Copyright © 2020-2023  润新知