poj1011

c++的实现方式可以参考http://blog.csdn.net/lyy289065406/article/details/6647960

解题思路:

DFS+剪枝

 

POJ2362的强化版,重点在于剪枝

 

令InitLen为所求的最短原始棒长,maxlen为给定的棒子堆中最长的棒子,sumlen为这堆棒子的长度之和,那么InitLen必定在范围[maxlen,sumlen]中

 

根据棒子的灵活度(棒子越长,灵活度越低) DFS前先对所有棒子降序排序

 

剪枝:

1、  由于所有原始棒子等长,那么必有sumlen%Initlen==0;

2、  若能在[maxlen,sumlen-InitLen]找到最短的InitLen,该InitLen必也是[maxlen,sumlen]的最短;若不能在[maxlen,sumlen-InitLen]找到最短的InitLen,则必有InitLen=sumlen;

3、  由于所有棒子已降序排序,在DFS时,若某根棒子不合适,则跳过其后面所有与它等长的棒子;

4、  最重要的剪枝:对于某个目标InitLen,在每次构建新的长度为InitLen的原始棒时,检查新棒的第一根stick[i],若在搜索完所有stick[]后都无法组合,则说明stick[i]无法在当前组合方式下组合,不用往下搜索(往下搜索会令stick[i]被舍弃),直接返回上一层

 


 import java.io.BufferedReader;

 import java.io.InputStreamReader;
 import java.util.Arrays;
 import java.util.StringTokenizer;


 public class Main {


     static boolean[] used;//用于记录组合时木棒的使用情况
     static int len;//截断后,木棒的根数
     static int[] s;//用来存储截断后木棒的长度
     static int sum;//所有木棒的总长度
     static int max;//木棒的最大长度
     static int parts;//理论上某种组合方式的木棒的根数


     public static void main(String[] args) throws Exception {
         BufferedReader read = new BufferedReader(new InputStreamReader(
                 System.in));
         
         //判断输入的木棒的根数是否为0.
         while ((len = Integer.parseInt(read.readLine()) ) != 0) {
            
        //根据截断后木棒的根数来创建一个整型数组
        s = new int[len];
             StringTokenizer take = new StringTokenizer(read.readLine());
             int index = 0;
             sum = 0;
             used = new boolean[len];
             
             //hasMoreTokens() .测试此 tokenizer 的字符串中是否还有更多的可用标记。
             while (take.hasMoreTokens()) {
             
            //nextToken().返回此 string tokenizer 的下一个标记。
            //将每一根木棒的长度存入s[]数组中
                 s[index] = Integer.parseInt(take.nextToken());
                 
                 //计算木棒的总长度
                 sum += s[index++];
             }
             
             //sort(int[] a) .对指定的 int 型数组按数字升序进行排序。
             Arrays.sort(s);
             
             //获得截断后木棒的最大长度
             max = s[len - 1];
             
             
             /**
              * 
              */
             
             //在[max,sum]之间寻找initlen
             for (; max <= sum; max++) {
                 
            //如果总长度sum对max整除
            if (sum % max == 0) {
             
            //计算以max为原始长度,这时候应该有多少根木棒
                     parts = sum / max;
                     
                     if (search(0, len - 1, 0)) {
                         System.out.println(max);
                         break;
                     }
                 }
             }
         }
     }


     /**
      * search(int res, int next, int cpl)主要用于寻找最短的原始棒长
      * 其主要思想包括以下几点:
      * 1)首先判断当前的组合长度res是否已经满足当前的原始棒长的可能值max,
      * 如果满足了,将组合长度res置为0,next置为len-2,已组合的木棒根数cpl加1.
      * 
      * 2)判断已组合的木棒根数cpl是否和理论上要组合木棒根数parts一致,
      * 如果一致,这证明已经找到最短原始长度,返回true
      * 
      * 3)如果进入到这一步,则证明还没有组合完成,这是重新开始组合一根木棒
      * 1))首先判断是否还有棒子用于组合,如果没有,则执行接下来的false.
      * 如果有,判断该棒子是否已经被使用过,如果被使用过,则使用下一根棒子,
      * 如果没有被使用过,则判断组合长度res+该棒子的长度s[next]是否<=当前最短原始棒长的可能值max
      * 如果不成立,则证明将剩下的棒子的长度都加起来,如果······
      * 
      * 
      * 
      * @param res
      * @param next
      * @param cpl
      * @return
      */
     public static boolean search(int res, int next, int cpl) {
         
    /**
     * res:已经组合的长度
     * next:要组合的木棒
     * cpl:已经组合的木棒的根数
     */
     
    /**
     * 如果组合的长度已经达到木棒的最大长度
     * 则进行下一轮组合:
     * 组合长度res置0
     * 已经组合好的木棒根数cpl的值+1
     * 确定下一根要组合的木棒
     */
    if (res == max) {
             res = 0;
             next = len - 2;
             cpl++;
         }
         
    /**
     * 如果已经组合好巅峰木棒数与理论上的相等,则
     * 返回true
     */
         if (cpl == parts) {
             return true;
         }
         
         //从大到小开始组合
         while (next >= 0){
         
        //如果next对应的木棒还没有使用
             if (used[next] == false) {
             
            //如果当前组合长度小于木棒的最大长度
                 if (res + s[next] <= max){
                 
                //将当前木棒的使用属性标识为true
                     used[next] = true;
                     
                     
                     /**
                      * 继续组合,如果下一轮组合刚刚好满足条件,则
                      * 返回true,组合完成
                      */
                     if (search(res + s[next], next - 1, cpl)) {
                         return true;
                     }
                     
                     /**
                      * 如果上面的if语句没有执行则证明,还没有组合完成
                      * 
                      */
                     
                     /**
                      * 如果执行到这里,则证明,当前木棒并不适合用于组合
                      * 将当前已经使用到的木棒的使用属性记为false
                      */
                     used[next] = false;
                     
                     /**
                      * 对于新棒的第一根棒子如果在搜索万所搜棒子以后都无法组合
                      * ,则证明该棒子在当前组合方式下无法组合
                      *所以,直接返回上一层 
                      */
                     if (res == 0) {
                         break;
                     }
                     
                     
                     if (res + s[next] == max) {
                         break;
                     }
                 }
                 
                 /**
                  *如果当前组合长度加上接下来的那一根木棒的长度大于最大长度
                  *,或者 已经完成这一次的组合,却还没有达到最大最大长度
                  */
                 
                 
                 int i = next - 1;
                 
                 /**
                  * while循环主要体现剪枝法:
                  * 如果当前木棒的长度不合适,那么
                  * 接下来的和当前木棒长度相同的木棒也不合适
                  * 
                  */
                 while (i >= 0 && s[i] == s[next]) {
                     i--;
                 }
                 next = i;
                 
                 
                 /**
                  * for循环的主要逻辑:
                  * 遍历接下来的剩下的所有木棒,
                  * 计算剩下所有木棒的总长度l_s
                  */
                 int l_s = 0;
                 for (int j = next; j >= 0; j--) {
                     if (!used[j]) {
                         l_s += s[j];
                     }
                 }
                 
                 /**
                  * if循环的主要逻辑:
                  * 如果实际剩下所有木棒的总长度l_s<
                  * 还需要的木棒的长度,则,跳出循环
                  * 证明
                  */
                 if (l_s < max - res) {
                     break;
                 }
                 
                 /**
                  * 剩下的木棒的总长度>还需要的木棒的长度,则
                  * 继续组装
                  */
                 continue;
             }
             
             //如果当前的木棒已经被使用过,将下标   - 1
             next--;
         }
         
         //如果遍历完所有的木棒都没有组合成功,则
         //返回false.
         return false;
     } 
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

帅气的东哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值