搜索,搜索,搜索..........
在经历了无数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.