• hdu 1455 Sticks(dfs+剪枝)


    题目大意:

      George有许多长度相同的木棍,随机的将这些木棍砍成小木条,每个小木条的长度都是整数单位(长度区间[1, 50])。现在George又想把这些小木棒拼接成原始的状态,但是他忘记了原来他有多少根木棍,也忘记了木棍的长度。现在请你编写一个程序,找到最短的原始的木棍长度。

    输入:

      每个测试案例包含两行,第一行表示小木条的总个数(最多64根小木条),第二行表示每个小木条的长度,用空格分开。当第一行输入为零的时候表示程序结束。

    输出:

      对于每个测试案例输出最短的木棍长度。

    解题思路:

      用搜索算法去解决问题需要知道搜索的区间和剪枝条件,现在木棍长度未知,木棍个数未知,对于我们搜索是不利的。需要从已知条件推导到一个可以搜索的区间,然后再仔细思考如何剪枝来减少时间复杂度。

      小木条是从木棍上砍下来的 => 木棍长度的下限:最长的小木条长度(part_max)

      小木条的个数和长度已知    => 木棍长度的上限:所有小木条长度之和(part_sum)

      确定搜索区间:木棍长度(stick_len) ∈ [part_max, part_sum]  并且 stick_len ∈ 整数

      确定了stick_len => 确定了木棍的个数(stick_num)

      剪枝1:当确定一个stick_len时候,那么这个stick_len要能整除part_sum,否则不能组成整数stick_num

      剪枝2:见AC代码分析

      剪枝3:见AC代码分析

      

    AC代码:

      1 // 19472897    2017 - 01 - 02 12:45 : 00    Accepted    1455    15MS    1724K    2328 B    C++    潮州牛肉丸
      2 #include <stdio.h>
      3 #include <algorithm>
      4 #include <functional>
      5 
      6 int n;                 // the number of parts
      7 int part[64];         // the parts at most 64
      8 int visit[64];         // mark the situation of already used parts
      9 int part_sum = 0;     // 所有小木条的长度之和
     10 int stick_num = 0;   // 木棍的个数
     11 int stick_len = 0;   // 木棍的长度
     12 
     13 void init()
     14 {
     15     part_sum = 0;
     16     stick_num = 0;
     17     memset(part, 0, sizeof(part));
     18     memset(visit, 0, sizeof(visit));
     19 }
     20 
     21 // count:已经拼接好的木棍数,len:正在拼接的木棍长度,index:上一次搜索下标位置
     22 bool dfs(int count, int len, int index)
     23 {
     24     if (stick_num == count)
     25         return true;
     26 
     27     for (int i = index + 1; i < n; ++i)
     28     {
     29         if (true == visit[i])
     30             continue;
     31         if (len + part[i] == stick_len)
     32         {
     33             visit[i] = true;
     34             if (true == dfs(count + 1, 0, -1))
     35                 return true;
     36             visit[i] = false;
     37             return false;
     38         }
     39         else if (len + part[i] < stick_len)
     40         {
     41             visit[i] = true;
     42             if (true == dfs(count, len + part[i], i))
     43                 return true;
     44             visit[i] = false;
     45 
     46             /* 已知条件:
     47                1、stick的长度(stick_num)和个数(stick_num)
     48                2、dfs已经返回false
     49             */
     50 
     51             /* 剪枝2:当dfs返回false,len等于0的时候,stick的长度不是题目的正确结果,
     52                后面的搜索无意义,直接返回false。
     53             
     54                根据原则:如果当前搜索用的stick长度是题目的正确结果,那么所有的part都一定要被用上。
     55                然而拼接一根新stick的时候(即len的值为0,表示当前正在拼接一根新stick),选取
     56                的第一根part无论如何都能被用上,只是后面的part去配合第一根part完成一根stick,
     57                如果在用第一根part的时候,dfs任然返回false,表示这个part不能被用上拼接stick。
     58                与原则相违背。
     59             */
     60             if (0 == len)
     61                 return false;
     62 
     63             /* 剪枝3:
     64                当前状态:dfs返回false,说明part[i]这根木条在当前count,len状态不可用,
     65                接下来搜索的part[i+1]如果与之前的part[i]长度相同也一定不可用,注意我们是把part按降序排序过的*/
     66             while (i < n && part[i] == part[i + 1])
     67                 ++i;
     68         }
     69     }
     70     return false;
     71 }
     72 
     73 int main(void)
     74 {
     75     while (scanf("%d", &n) != EOF && 0 != n)
     76     {
     77         init();
     78         for (int i = 0; i < n; ++i)
     79         {
     80             scanf("%d",    &part[i]);
     81             part_sum += part[i]; //记录所有小木条长度之和
     82         }
     83 
     84         std::sort(part, part + n, std::greater<int>()); // 按照降序排序
     85         
     86         // stick的可能长度区间:[最长的小木条长度,所有小木条长度之和]
     87         for (stick_len = part[0]; stick_len < part_sum; ++stick_len) 
     88         {
     89             if (0 == part_sum % stick_len) //剪枝1:木棍的长度一定要能整除所有小木条长度之和
     90             {
     91                 stick_num = part_sum / stick_len; // 得到stick的个数
     92                 if (true == dfs(0, 0, -1))
     93                     break;
     94             }
     95         }
     96 
     97         printf("%d
    ", stick_len);
     98     }
     99     return 0;
    100 }
  • 相关阅读:
    数字问题-LeetCode 462、463、473、474、475、476、477、482(二分)
    数字问题-LeetCode 452、453、454、455、456、459(KMP算法)
    Python之多进程、多线程---王要胜---2020-01-01
    生活与思考
    英文每日记录
    运维面试题五十题
    利用阿里云ECS跳板机内网穿透- ssh
    曾经的自己
    python 开发必备知识
    人生路上的思考
  • 原文地址:https://www.cnblogs.com/yongqiang/p/6242371.html
Copyright © 2020-2023  润新知