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