【搜索】一道不错的搜索题(关于优化)

问题

【题目描述】

某人真的要去种菜菜了。

某人承包了一片土地,并决定在这片土地上建造一块属于自己的菜园,为了防止大牛们来菜园偷菜,某人决定给菜园围上栅栏。

某人的菜园是正方形的,当前有一些建造栅栏的木材,某人想把这些木材全部用完来建造栅栏,且木材不能锯断。

【输入格式】

第一行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.

反思

搜索需要状态表示和状态转移(类似动态规划)。不同的状态表示往往可以很大程度上决定程序的运行速度,要仔细考虑,并不是只要能表示当前状态的状态表示就是最优的!

posted @ 2011-06-16 09:54  liukee  阅读(287)  评论(0编辑  收藏  举报