【搜索】一道不错的搜索题(关于优化)
问题
【题目描述】
某人真的要去种菜菜了。
某人承包了一片土地,并决定在这片土地上建造一块属于自己的菜园,为了防止大牛们来菜园偷菜,某人决定给菜园围上栅栏。
某人的菜园是正方形的,当前有一些建造栅栏的木材,某人想把这些木材全部用完来建造栅栏,且木材不能锯断。
【输入格式】
第一行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.
反思
搜索需要状态表示和状态转移(类似动态规划)。不同的状态表示往往可以很大程度上决定程序的运行速度,要仔细考虑,并不是只要能表示当前状态的状态表示就是最优的!
浙公网安备 33010602011771号