poj1011 stick 强力搜索剪枝

搜索,搜索,搜索..........

在经历了无数tle后,终于也很慢很慢的速度ac了它.......大概有15ms吧,光是在pascal中就排了200多名。


题目比较简单:给你若干个值不超过50,数量不超过60的自然数,把给出的所有的数用完且只用一次,使其拼成若干个相等的自然数;

                            求这个最小的自然数。(题目中表示为木条长度)


在想贪心,想贪心,最后无语发现,它是搜索。


搜索的想法很简单,枚举这个自然数,然后搜索是否能拼成。

闭着眼睛都可以知道这是会tle的,于是,截肢.........不,说错了,是剪枝.........(有点血腥);


首先是一贯作风,搜索中糅合贪心,可以发现,把木棍从大到小排,按这个顺序取是容易得到最优值的----------------剪枝1

然后,发现在拼一根木棍时,可以记录一下,保证是从左往右取的(或者说从大往小),这样不会漏掉情况,可以少搜一点点.....--------------剪枝2

随后,如果某一个木棍拼完之后,其他的拼不了了,就可以剪掉这以后的搜索了,因为看看我们的搜索顺序就知道了-----------------剪枝3

一开始有小到大枚举最优值比较好,但是发现最后从大到小反而比较稳定,因为任何一个 可能作为最优值的数(是给出的数的总和的约数),那它的约数也挂定了--------剪枝4


本以为可以了, 于是一直tle,tle,tle,与此同时,学校分类练习题中这道题的答案被声称数据错误,并且标程也错了,标程中也惊现不知是剪枝还是错误的东东............

无限纠结中...............

终于,在某位不知名大牛的博客里寻得了第5个强力剪枝————在拼某一个数(木条)时,第一个用来拼的数确定之后,如果找不到方案,以后的就不要搜了(很明显,那个 没用的木条最后还是要面对无法配对的命运的.........)


终于 ,以此A掉了它........同时证明了分类练习题中的数据是正确的............感动的偶想要热泪盈眶,毕竟是纠结了一下午的题目。

stick可谓是剪枝发挥到很大极限的了,搜索的剪枝一般思路是贪心,即从某个状态以后不可能更优,甚至不可能可行,然后极不留情的剪掉它,剪掉它.........


贴代码:

program lmd;
var
   can:array[0..10000]of boolean;
   a:array[0..100]of longint;
   have:array[0..100]of boolean;
   i,tot,n,try,ans,z,tt,m:longint;
   yes,bug:boolean;
procedure sort(l,r:longint);
var i,j,x,s:longint;
begin
  i:=l;j:=r;x:=a[(l+r)shr 1];
  repeat
    while a[i]>x do inc(i);
    while a[j]<x do dec(j);
    if i<=j then
      begin
        s:=a[i];a[i]:=a[j];a[j]:=s;
        inc(i);dec(j);
      end;
  until i>j;
  if i<r then sort(i,r);
  if l<j then sort(l,j);
end;
procedure dfs(num,now,pos:longint);//pos 剪枝2
var i:longint;
begin
    if num>z then begin yes:=true;exit;end;
    for i:=pos+1 to n do
    if have[i] then
      begin
        if a[i]+now=try then
           begin
             have[i]:=false;
             dfs(num+1,0,0);
             have[i]:=true;
             exit;                  //剪枝3
           end
        else if a[i]+now<try then
          begin
              have[i]:=false;
              dfs(num,now+a[i],i);
               have[i]:=true;
               if (now=0) then exit;//终极大招剪枝5
              if yes then exit;
          end;
      end;
end;
begin
   while true do begin
    read(m);
    if m=0 then break;
    tot:=0; n:=0;
    for i:=1 to m do
    begin
      read(tt);
      if tt<=50 then begin
        inc(n);
        a[n]:=tt;
        tot:=tot+a[n];
        end;
    end;
    sort(1,n);                      //剪枝1
    fillchar(can,sizeof(can),true);
    for try:=tot downto a[1] do     
     begin
       if can[try] and (tot mod try=0) then     
         begin
           yes:=false;z:=tot div try;     
           fillchar(have,sizeof(have),true);
           dfs(1,0,0);
           if yes=true then
             ans:=try
              else
                begin
                  for i:=a[n] to try-1 do            //剪枝4
                    if try mod i=0 then
                       can[i]:=false;
                end;
         end;
     end;
     write(ans);
  end;
end.





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值