zoukankan      html  css  js  c++  java
  • Sticks(poj1011/uva307)

    题目大意:

    乔治有一些碎木棒,是通过将一些相等长度的原始木棒折断得到的,给出碎木棒的总数和各自的长度,求最小的可能的原始木棒的长度;(就是将一些正整数分组,每组加起来和相等,使和尽可能小)

    一开始做poj 32ms过,但uva3000 ms 都超时。。。而且poj discuss里给出了一组bT数据,最后uva 0.2sac的代码也跑了3 。4秒左右。discuss里的大牛据说什么奇偶性剪枝0.01ms过,可惜搜了半天也没找到具体方法。这题就这样过好了。。(参考各种空间博客才艰难ac。不过此题实在是太经典了,搜索学习剪枝必做) 

    对于这道题而言,剪枝的策略一般有下面6个:

    ①先将木棒长度从大到小进行排序,这样便于后面的选择和操作,是后面一些剪枝算法的前提。

    ②在枚举原木棒长度时,枚举的范围为max与sum/2之间,如果这个区间内没有找到合适的长度,那么最后原木棒的长度只能是sum。

    ③枚举的原木棒的长度只能是sum的约数。

    ④在深搜过程中,如果当前木棒和前一个木棒的长度是一样的,但是前一个木棒没有被选上,那么这个木棒也一定不会被选上。

    ⑤在深搜过程中,如果当前是在拼一根新木棒的第一截,但如果把可用的最长的一根木棒用上后不能拼成功的话,那么就不用再试后面的木棒了,肯定是前面拼的过程出了问题。

    ⑥在深搜过程中,如果当前可用的木棒恰好能补上一根原木棒的最后一截,但用它补上之后却不能用剩下的木棒完成后续的任务,那么也不用再试后面的木棒了,肯定是前面拼的过程出了问题。

    参考博客:http://www.cnblogs.com/staginner/archive/2011/09/08/2171329.html

    我的AC 代码:(剪枝见注释)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
     
    int n,max_len,min_len,sum,ans,num,flag;
    int a[10000],v[10000];
     
    bool cmp(const int &x,const int &y)
    {
         return !(x<y);
    }
     
    void search(int step,int from,int cur)//step表示当前去凑的是第几根原始棒,order表示当前用了多少根碎棒子来凑第step根
    {
         //printf("%d %d ",step,cur);
        if (cur==ans)
        {
           if (step==num)
           {
              flag=1;
              return;
           }
           int k;
           for (k=1;k<=n && v[k];k++);
           v[k]=1;
           search(step+1,1,a[k]);
           v[k]=0;
           return;
        }
        for (int i=from;i<=n;i++)
        {
            if (v[i])
               continue;
            if (cur+a[i]>ans)
               continue;
            if (a[i]==a[i-1] && !v[i-1])
               continue;//剪枝3:上一根棒和目前这根一样并且没用过,那么用这一根去凑和用上一根去凑结果一样都不能出解
            //printf("kk %d %d ",cur,cur+a[i]);
            int t=a[i]+cur;
            if (t!=ans && t+min_len>ans)
               continue;
            v[i]=1;
            search(step,i+1,t);
            v[i]=0;
            if (t==ans)
               return;
            if (flag || cur==0)//剪枝4:如果当前用了1根碎棒子来凑,枚举了第一根碎棒不能出解那么跳出
               return;
        }
        return;
    }
     
    int main ()
    {
        while (scanf("%d",&n)==1)
        {
              if (n==0)
                 break;
              v[0]=1;
              a[0]=210000000;
              max_len=-1;
              min_len=21000000;
              sum=0;
              flag=0;
              for (int i=1;i<=n;i++)
              {
                  scanf("%d",&a[i]);
                  if (a[i]>max_len)
                     max_len=a[i];
                  if (a[i]<min_len)
                     min_len=a[i];
                  sum+=a[i];
              }
              sort(a+1,a+n+1,cmp);
              for (ans=max_len;ans<=sum/2;ans++)//剪枝1:答案的下界为最长的棒,上界为长度总和
              {
                  if (sum%ans!=0)
                     continue;//剪枝 2:原始棒长度是总长度的约数
                  for (int k=1;k<=n;k++)
                      v[k]=0;
                  num=sum/ans;
                  search(1,1,0);
                  if (flag)
                  {
                      printf("%d ",ans);
                      break;
                  }
              }
              if (!flag)
                 printf("%d ",sum);
        }
        return 0;
    }
    Every day is meaningful, keeping learning!
  • 相关阅读:
    第二十九课 循环链表的实现
    第二十八课 再论智能指针(下)
    第二十七课 再论智能指针(上)
    第二十六课 典型问题分析(Bugfix)
    普通new和placement new的重载
    leetcode 581. Shortest Unsorted Continuous Subarray
    leetcode 605. Can Place Flowers
    leetcode 219. Contains Duplicate II
    leetcode 283. Move Zeroes
    leetcode 217. Contains Duplicate
  • 原文地址:https://www.cnblogs.com/vb4896/p/3874620.html
Copyright © 2011-2022 走看看