【数据结构•hash表】烦恼的设…

本文探讨了在园林设计中遇到的挑战,即如何通过最少的操作数调整花坛布局,使之符合预定设计。通过分析问题规模、提出广度优先搜索算法并巧妙运用哈希表进行状态判重,实现高效求解。本文详细阐述了解题思路、算法设计与转换过程,旨在提供解决复杂布局优化问题的方法论。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【数据结构•hash表】烦恼的设计师(SGOI)

Time Limit:10000MS  Memory Limit:65536K
Total Submit:30 Accepted:7

Description

  烦恼的设计师(来自SGOI)
  春天到了,百花齐放,西湖公园里新设置了许多花坛,设计师想用不同的花摆出不同的图案以吸引游人,于是设计了各种图案并且在花圃中选好了要摆放的花。不幸的是负责搬运和摆放的工人因为临时有事,只将花放到花架上就匆匆离开了,并没有按照设计师原来的设计方案摆放,结果花坛杂乱不堪,设计师只好自己来调整花的位置。由于设计师通常从事脑力劳动,较少从事搬运和摆放花盆的体力工作,所以请你帮忙找出一种移动方法使工作量最小。
  不同种类的花有不同的类型编号,虽然地球上花的种类很多,但因为公园里的花不超过1,000,000种,所以花的类型编号不超过1,000,000。另一方面,出于美学考虑,一个花坛里摆放的不同种类的花不超过3种,且不同种类的花的数量不可太接近,对于任意两种花,数量多的花的盆数至少是数量少的花的2倍。
  花坛是正六边形的,共摆放有19盆花,每盆花都放在一个转盘上,转动一盆花下面的转盘,会使周围的6盆花顺时针或逆时针移动一个位置(但不可把花转到花坛外),称为一次操作。你的任务:用最少的操作使花坛由初始状态转化为符合设计图纸的目标状态。例如:

【数据结构•hash表】烦恼的设计师(SGOI)pascal <wbr>测试数据 <wbr>解题报告
  

初始状态              目标状态


  如图,只需将处于圆心位置的那盆花的转盘顺时针转动一个位置,红色的花就移动到了目标位置。

Input

  输入文件共11行,1至5行描述花坛的初始状态,7至11行表示花盆应摆放的位置。中间以空行分隔,5行数字分别表示花坛的5个行,其中第1、5两行有3个整数,第2、4两行有4个整数,第3行有5个整数,表示每一行的花的类型,不同的数代表不同种类的花。

Output

  输出文件,一行,包含一个整数,即最少的操作数,数据保证20步之内有解。

Sample Input

1 1 1
1 2 1 1
1 1 1 1 1
1 1 1 1
1 1 1

1 1 1
1 1 1 1
1 1 2 1 1
1 1 1 1
1 1 1

Sample Output

1

Hint

题意描述:
  给定两个正6边形的花坛,要求求出从第一个变化到第二个的最小操作次数以及操作方式。一次操作是:选定不在边上的一盆花,将这盆花周围的6盆花按照顺时针或者逆时针的顺序依次移动一个单位。限定一个花坛里摆放的不同种类的花不超过3种,对于任意两种花,数量多的花的盆数至少是数量少的花的2倍 。(这是 SGOI-8 的一道题)

解题分析:
  首先确定本题可以用广度优先搜索处理,然后来看问题的规模。正6边形共有19个格子可以用来放花,而且根据最后一句限定条件,至多只能存在 C(2,19) * C(5,17) = 1058148 种状态,用搜索完全可行。然而操作的时候,可以预料产生的重复节点是相当多的,需要迅速判重才能在限定时间内出解,因此想到了哈希表。那么这个哈希函数如何设计呢?注意到19个格子组成6边形是有顺序的,而且每一个格子只有3种可能情况,那么用3进制19位数最大 3^20-1=3486784400,注意,这个数值大于2^31,但小于2^32。于是我们将每一个状态与一个整数对应起来,使用除余法就可以了。

 

 

解题分析里已经写得很清楚了。但写起来仍然很麻烦,尤其是转换那里。

 

const
 maxn=1058148;
 m=1375571;

var
 s,sum:longint;
 head,tail:longint;
 a:array[0..19]of shortint;
 f:array[0..maxn,0..19]of shortint;
 h:array[0..m+1]of longint;

function ha(x:longint):longint;//直接取余法
begin
 ha:=x mod m;
end;

function locate(x:longint):longint;//x在哈希表中位置
var
e,i:longint;
begin
 e:=ha(x);
 i:=0;
 while (h[(e+i)mod m]<>0)and(h[(e+i)mod m]<>x)and(i<m) do inc(i);
 locate:=(e+i) mod m;
end;

procedure hash;//判断是否在哈希表中,若不在则加入广搜数组
var
 i,l:longint;
begin
 l:=locate(sum);
 if h[l]<>sum then
  begin
  h[l]:=sum;
  inc(tail);
  f[tail]:=a;
  f[tail,0]:=f[head,0]+1;
  end;
end;

procedure jinzhi;//把三进制转化为十进制
var
 i,k:longint;
begin
 sum:=0;
 k:=1;
 for i:=19 downto 1 do
  begin
  sum:=sum+a[i]*k;
  k:=k*3;
  end;
 if s=0 then exit;//第一次运行这个函数是为了找出 最终目的 的十进制数
 if sum=s then begin write(f[head,0]+1); halt; end;
 hash;
end;

procedure init;//读入,并把读入的数转化为 1,2,3
var
 i,j,k:longint;
 mark:array[1..3]of longint;
 aa:array[1..19]of longint;
begin
 fillchar(mark,sizeof(mark),0);

 for i:=1 to 19 do read(aa[i]);

 k:=0;
 for i:=1 to 19 do
  begin
  j:=1;
  for j:=1 to k do
   if mark[j]=aa[i] then break;
  if mark[j]<>aa[i] then begin inc(k); mark[k]:=aa[i]; end;
  if k=3 then break;
  end;

 for i:=1 to 19 do
  for j:=1 to 3 do
  if aa[i]=mark[j] then
  begin
   f[1,i]:=j;
   break;
  end;

 for i:=1 to 19 do read(aa[i]);
 for i:=1 to 19 do
  for j:=1 to 3 do
  if aa[i]=mark[j] then
  begin
   aa[i]:=j;
   break;
  end;

 for i:=1 to 19 do a[i]:=aa[i];
 jinzhi;
 s:=sum;

 head:=0;


 f[0,0]:=-1;
 a:=f[1];
 jinzhi;//这三步是为了判断读入的两种情况是否相同
end;

procedure change1(x:longint);//顺时针旋转
var
 p:longint;
begin
 if (4<x)and(x<7) then
 begin
 p:=a[x-1];
 a[x-1]:=a[x+4];
 a[x+4]:=a[x+5];
 a[x+5]:=a[x+1];
 a[x+1]:=a[x-3];
 a[x-3]:=a[x-4];
 a[x-4]:=p;
 end;
 if (8<x)and(x<12) then
  begin
 p:=a[x-1];
 a[x-1]:=a[x+4];
 a[x+4]:=a[x+5];
 a[x+5]:=a[x+1];
 a[x+1]:=a[x-4];
 a[x-4]:=a[x-5];
 a[x-5]:=p;
 end;
 if (13<x)and(x<16) then
  begin
 p:=a[x-1];
 a[x-1]:=a[x+3];
 a[x+3]:=a[x+4];
 a[x+4]:=a[x+1];
 a[x+1]:=a[x-4];
 a[x-4]:=a[x-5];
 a[x-5]:=p;
 end;
end;

procedure change2(x:longint);//逆时针旋转
var
 p:longint;
begin
 if (4<x)and(x<7) then
 begin
 p:=a[x-1];
 a[x-1]:=a[x-4];
 a[x-4]:=a[x-3];
 a[x-3]:=a[x+1];
 a[x+1]:=a[x+5];
 a[x+5]:=a[x+4];
 a[x+4]:=p;
 end;
 if (8<x)and(x<12) then
 begin
 p:=a[x-1];
 a[x-1]:=a[x-5];
 a[x-5]:=a[x-4];
 a[x-4]:=a[x+1];
 a[x+1]:=a[x+5];
 a[x+5]:=a[x+4];
 a[x+4]:=p;
 end;
 if (13<x)and(x<16) then
 begin
 p:=a[x-1];
 a[x-1]:=a[x-5];
 a[x-5]:=a[x-4];
 a[x-4]:=a[x+1];
 a[x+1]:=a[x+4];
 a[x+4]:=a[x+3];
 a[x+3]:=p;
 end;
end;

procedure bfs;//广搜
var
 i:longint;
begin
 head:=1;
 tail:=1;
 while true do
  begin
  a:=f[head];
  change1(5);
  jinzhi;
  a:=f[head];
  change1(6);
  jinzhi;
  a:=f[head];
  change1(9);
  jinzhi;
  a:=f[head];
  change1(10);
  jinzhi;
  a:=f[head];
  change1(11);
  jinzhi;
  a:=f[head];
  change1(14);
  jinzhi;
  a:=f[head];
  change1(15);
  jinzhi;

  a:=f[head];
  change2(5);
  jinzhi;
  a:=f[head];
  change2(6);
  jinzhi;
  a:=f[head];
  change2(9);
  jinzhi;
  a:=f[head];
  change2(10);
  jinzhi;
  a:=f[head];
  change2(11);
  jinzhi;
  a:=f[head];
  change2(14);
  jinzhi;
  a:=f[head];
  change2(15);
  jinzhi;

  inc(head);
  end;
end;

begin
 init;
 bfs;
end.


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值