【搜索】一道不错的搜索题(关于优化)
问题
【题目描述】
某人真的要去种菜菜了。
某人承包了一片土地,并决定在这片土地上建造一块属于自己的菜园,为了防止大牛们来菜园偷菜,某人决定给菜园围上栅栏。
某人的菜园是正方形的,当前有一些建造栅栏的木材,某人想把这些木材全部用完来建造栅栏,且木材不能锯断。
【输入格式】
第一行N 表示有N组数据;
接下来N行,每行第一个数字M,表示木材的数量,接下来M个数字ai表示木材的长度。
【输出格式】
对于每一组数据输出一行结果
如果可以围成栅栏,输出‘Yes’;如果不可以,输出‘No’
【输入样例】
3
4 1 1 1 1
5 10 20 30 40 50
8 1 7 2 6 4 4 3 5
【输出样例】
Yes
No
Yes
【数据范围】
保证没有均为‘Yes’or‘No’的哦。^.^.
30% 1<=N<=10 4<=M<=10
100% 1<=N<=200 4<=M<=20 0<=AI<=1000
分析
我们可以发现如果这n个木材能够围城一个正方形的菜园,那么其长度和必定是4的倍数,且其能构成的正方形是唯一的,边长为sum div 4
知道了边长之后问题就得到了简化。
首先,如果给定的边长中存在大于最终边长的木材,直接输出no
剩下的问题就是dfs了,当然,直接搜是不行的,需要一定的剪枝:
1、只要构造出三条边,剩下的必然能够构造出第四条边(因为sum=bianchang*4)
2、如果当前正在构造的边长不足目标边长时,才继续构造。
dfs(last,now,already)last,表示上一次到了第last块木块,由于搜索构造相当于从n块木块中选择p块构成目标边长,也就是可以看成是一种组合,我们默认后面选取的都比前边的标号大就可以删去好多没用的状态,这个状态表示对程序的速度起到了举足轻重的作用,效果极其明显。
now表示当前正在构造的边长已有长度为多少
already表示已经构造的边长个数是多少
程序就变得很easy了
program liukeke; var a:array[1..21] of longint; n,m,zu,sum,bc,i,j,tt,max:longint; flag:boolean; used:array[1..21] of boolean; procedure sort(l,r:longint); var i,j,mid,temp:longint; begin i:=l; j:=r; mid:=a[(l+r)>>1]; repeat while a[i]>mid do inc(i); while a[j]<mid do dec(j); if i<=j then begin temp:=a[i]; a[i]:=a[j]; a[j]:=temp; inc(i); dec(j); end; until i>j; if l<j then sort(l,j); if i<r then sort(i,r); end; procedure dfs(last,now,already:longint); var temp,i:longint; begin if flag then exit; if already=3 then begin writeln('Yes'); flag:=true; exit; end; for i:=last+1 to m do if not used[i] then begin temp:=now+a[i]; if temp=bc then begin used[i]:=true; dfs(0,0,already+1);//再次构造是重头开始 if flag then exit; used[i]:=false; end; if temp<bc then begin used[i]:=true; dfs(i,temp,already); if flag then exit; used[i]:=false; end; end; end; begin assign(input,'fence.in');reset(input); assign(output,'fence.out');rewrite(output); readln(n); for zu:=1 to n do begin read(m); sum:=0; bc:=0; max:=0; for i:=1 to m do begin read(a[i]); if a[i]>max then max:=a[i]; inc(sum,a[i]); end; if (sum mod 4)<>0 then//很显然,不解释 begin writeln('No'); continue; end else bc:=sum div 4;//边长固定!!! if max>bc then begin writeln('No'); continue; end; sort(1,m); flag:=false; fillchar(used,sizeof(used),0); dfs(0,0,0); if not flag then writeln('No'); end; close(input); close(output); end.
反思
搜索需要状态表示和状态转移(类似动态规划)。不同的状态表示往往可以很大程度上决定程序的运行速度,要仔细考虑,并不是只要能表示当前状态的状态表示就是最优的!