一个程序员杂志上关于推箱子的问题(200分)

  • 主题发起人 主题发起人 charlyisme
  • 开始时间 开始时间
C

charlyisme

Unregistered / Unconfirmed
GUEST, unregistred user!
一个程序员杂志上关于推箱子的问题
只有一个箱子。精灵须将箱子推到目标处。
要求:
操作精灵从初始地点出发,通过走或者推将箱子推到目标格。
要求推的次数尽量少,对于推的次数相同的策略中,要求走的次数尽量少。
迷宫地图文件为boxman.ini
第一行 为r,c 表示迷宫的行数与列数
以下的r行有c个字符
#岩石 .空格 S精灵 B箱子 T目标格
输出
boxman.out 移动序列 NSEW nsew(方向)大写表示推,小写表示走
程序员杂志最后给了个伪码,没有看懂,呵呵,小弟不知道如何具体实现,请哪位清楚这个问题的朋友给我一个能运行的代码(加一点注释)
谢谢!
 
那个问题啊,我提交代码了而且通过了测试。
不过源代码已经跟我的硬盘同归于尽了。
不过我用的方法跟杂志上说的不一样。
我的方法是让精灵竞赛,没循环一次每一个精灵移动一步,
同时淘汰慢的精灵,最后第一个到达终点的精灵就是最快的。
 
呵呵,前两年闲的没事的时候编过一个推箱子的游戏.
印象中是可以自己编辑地图,可以自己推,也可以让电脑帮你推,求了最短路径,但效率不是很好.
上班后翻翻箱底,找到就给你寄去.如果找不到,就重新编一个针对本题的.
问两句:1.地图的规模.
2.听Tassadar的意思精灵还不止一个,如果真的是不止一个还要求最优解的话,
就很为难了.关键是时间复杂度受不了.
 
精灵是不只一个,但是优胜者只有一个,当走最短路线的精灵走到终点的时候
最优解就出来了,效率的优化关键是把落后的精灵杀掉,尽量保持最少的精灵在
竞赛,我当时的算法大概就是把撞到墙的,跑到别人已经跑过的格子精灵杀掉。
结果效率还是不错的
 
我怎么觉得楼主和Tassadar描述的是两个不同的题目呢?
 
没错就是那到题目《程序员》杂志上还有我的名字呢
 
呵呵,我漏看了一个条件"只有一个箱子",
如果只有一个箱子,这题就简单了好几个数量级 .
 
你可以先参考一下我的关于最短路径的笔记.
如果还有困难,请确定以下问题:
1.是否有多个精灵
2.迷宫的规模
3.如果有多个精灵的话,两个精灵是否能同时进入同一地点.
 
我花了一点时间写了出来了,不过算法跟杂志的有点不一样
要就留个mail
 
如果是跟文曲星上的推箱子一样的话,金牌之路题解上有伪代码
 
to AI:
我一开始也误解了楼主的意思,他的题目不是典型的推箱子游戏,他只有一个箱子.
 
实际上只不过是找最短路径而已
 
呵呵,只有一个精灵,一个箱子呀,就是要将精灵将箱子推到目标格。
地图是读进去的呀。
好像这不仅仅是最短路径的问题,题目伪码提到了状态图的转换,
从伪码上看,求最短路径只是其中一个函数。
还要请教各位朋友,希望能给我一个具体实现的源码。
email:charlyisme@tom.com
 
我知道地图是读进去的.
但是,如果地图是20*30的或者是2000000*30000000的不能用一个算法的.
 
写得比较仓促,效率好像没以前写的好了
给大家看看,有什么改进方法
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TMoveDir = (mdUp, mdDown, mdLeft, mdRight);//移动方向
TMoveObject = class;
TMoveObjects = array of TMoveObject;//移动对象数组
//移动对象类,是精灵和箱子的父类
TMoveObject = class
private
FActive: Boolean;
procedure SetActive(const Value: Boolean);
protected
FTarget : TPoint;
Winner : TMoveObject;
FTurnCount : Integer;
FObjectList : TList;
FRow,
FColumn : Integer;
FMarks : TMoveObjects;
FMap : TStrings;
FPos : TPoint;
FTrace : String;
public
property Active : Boolean read FActive write SetActive;
function Move : Boolean;
virtual;
abstract;//移动方法
constructor Create(ObjectList : TList;
Row, Column : Integer;
Map : TStrings;
Marks : TMoveObjects;
Target : TPoint);
end;

//精灵对象
TSpirit = class(TMoveObject)
private
FBoxPos : TPoint;
public
constructor Create(ObjectList : TList;
Row, Column : Integer;
Map : TStrings;
Marks : TMoveObjects;
Target : TPoint;
BoxPos : TPoint);
reintroduce;
function Move : Boolean;
override;
end;

//箱子对象
TBox = class(TMoveObject)
private
FSpiritPos : TPoint;
function FMoveSpirit(FromPos, Target : TPoint;
var Trace : String;
BoxPos : TPoint) : Boolean;//移动精灵
procedure SetSpiritPos(const Value: TPoint);
protected
FStepCount : Integer;
public
constructor Create(ObjectList : TList;
Row, Column : Integer;
Map : TStrings;
Marks : TMoveObjects;
Target : TPoint);
reintroduce;
function Move : Boolean;
override;//移动箱子
property SpiritPos : TPoint read FSpiritPos write SetSpiritPos;
end;

TForm1 = class(TForm)
Memo1: TMemo;
Button1: TButton;
Memo2: TMemo;
procedure Button1Click(Sender: TObject);
private
procedure FShowAction(Trace : String);//显示移动动画
function FGetShortCut : String;//获取最短路径
public
end;

var
Form1: TForm1;
implementation
uses Types;
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var
TickCount : Integer;
S : String;
OldText : String;
begin
TickCount := GetTickCount;
S := FGetShortCut;
Caption := FloatToStr((GetTickCount - TickCount)/1000);
if S = '' then
begin
ShowMessage('Sorry, I can''tdo
it');
Exit;
end;
OldText := Memo1.Lines.Text;
FShowAction(S);
Memo1.Lines.Text := OldText;
end;

{ TMoveObject }
constructor TMoveObject.Create(ObjectList : TList;
Row, Column : Integer;
Map : TStrings;
Marks : TMoveObjects;
Target : TPoint);
begin
FObjectList := ObjectList;
ObjectList.Add(Self);
FActive := True;
FTurnCount := 0;
FRow := Row;
FColumn := Column;
FTrace := '';
FMap := Map;
FMarks := Marks;
FTarget := Target;
end;

procedure TMoveObject.SetActive(const Value: Boolean);
begin
FActive := Value;
end;

{ TSpirit }
constructor TSpirit.Create(ObjectList: TList;
Row, Column: Integer;
Map: TStrings;
Marks: TMoveObjects;
Target: TPoint;
BoxPos: TPoint);
begin
inherited Create(ObjectList, Row, Column, Map, Marks, Target);
FBoxPos := BoxPos;
end;

function TSpirit.Move : Boolean;
var
OldPos : TPoint;
aSpirit,
RaceSpirit : TSpirit;
OldTrace : String;
OldTurnCount : Integer;
function FMove(Dir : TMoveDir) : Boolean;
var
NewTurnCount : Integer;
NewTrace : String;
NewPos : TPoint;
begin
Result := False;
NewPos.X := OldPos.X;
NewPos.Y := OldPos.Y;
NewTurnCount := OldTurnCount;
//根据不同方向移动精灵
case Dir of
mdUp : begin
Dec(NewPos.Y);
NewTrace := OldTrace + 'n';
if (OldTrace <> '') and (OldTrace[Length(OldTrace)] <> 'n') then
Inc(NewTurnCount);
end;
mdDown : begin
Inc(NewPos.Y);
NewTrace := OldTrace + 's';
if (OldTrace <> '') and (OldTrace[Length(OldTrace)] <> 's') then
Inc(NewTurnCount);
end;
mdLeft : begin
Dec(NewPos.X);
NewTrace := OldTrace + 'e';
if (OldTrace <> '') and (OldTrace[Length(OldTrace)] <> 'e') then
Inc(NewTurnCount);
end;
mdRight : begin
Inc(NewPos.X);
NewTrace := OldTrace + 'w';
if (OldTrace <> '') and (OldTrace[Length(OldTrace)] <> 'w') then
Inc(NewTurnCount);
end;
end;
if (NewPos.Y >= 0) and (NewPos.Y <= FRow - 1) and (NewPos.X >= 1) and (NewPos.X <= FColumn) //检查是不是出界
and not (FMap.Strings[NewPos.Y][NewPos.X] in ['#'])//有没有障碍物
and not ((NewPos.X = FBoxPos.X) and (NewPos.Y = FBoxPos.Y))//没碰到箱子
then
begin
//比较达到同一个格子的两个精灵,保留快的,停掉慢的
RaceSpirit := FMarks[NewPos.Y * FColumn + NewPos.X - 1] as TSpirit;
if RaceSpirit <> nil then
begin
if (RaceSpirit.FPos.X <> NewPos.X) or (RaceSpirit.FPos.Y <> NewPos.Y) then
Exit//如果格子所在的精灵的位置不在格子上,说明精灵早已经经过这个格子,新精灵就是慢了
else
if Length(RaceSpirit.FTrace) < Length(NewTrace) then
Exit//比较FTrace(移动记录)长度,短者优胜
else
if RaceSpirit.FTurnCount <= NewTurnCount then
Exit//比较转弯次数,少者优胜
else
begin
RaceSpirit.Active := False;
end;
end;
if (OldPos.X = FPos.X) and (OldPos.Y = FPos.Y) then
aSpirit := Self
else
aSpirit := TSpirit.Create(FObjectList, FRow, FColumn, FMap, FMarks, FTarget, FBoxPos);
with aSpiritdo
begin
FPos.X := NewPos.X;
FPos.Y := NewPos.Y;
FTurnCount := NewTurnCount;
FTrace := NewTrace;
end;
FMarks[NewPos.Y * FColumn + NewPos.X - 1] := aSpirit;//占领格子
Result := (aSpirit.FPos.X = FTarget.X) and (aSpirit.FPos.Y = FTarget.Y);//判断是否到达目的地
if Result then
Winner := aSpirit;//返回胜利者
end;
end;
begin
Result := False;
//保存旧资料
OldPos := FPos;
OldTrace := FTrace;
OldTurnCount := FTurnCount;
//分别向四个方向移动,移动之前先判断箱子不是向后退
if ((OldTrace = '') or (OldTrace[Length(OldTrace)] <> 's')) and FMove(mdUp) then
begin
Result := True;
Exit;
end;
if ((OldTrace = '') or (OldTrace[Length(OldTrace)] <> 'n')) and FMove(mdDown) then
begin
Result := True;
Exit;
end;
if ((OldTrace = '') or (OldTrace[Length(OldTrace)] <> 'w')) and FMove(mdLeft) then
begin
Result := True;
Exit;
end;
if ((OldTrace = '') or (OldTrace[Length(OldTrace)] <> 'e')) and FMove(mdRight) then
begin
Result := True;
end;
if (FPos.X = OldPos.X) and (FPos.Y = OldPos.Y) then
Active := False;
end;

{ TBox }
function TBox.Move: Boolean;
var
OldPos,
OldSpiritPos : TPoint;
aBox,
RaceBox : TBox;
OldTrace : String;
OldTurnCount,
OldStepCount : Integer;
function FMove(Dir : TMoveDir) : Boolean;
var
NewTurnCount,
NewStepCount : Integer;
NewTrace : String;
NewPos,
NewSpiritPos : TPoint;
SpiritTrace : String;
begin
Result := False;
NewPos := OldPos;
NewSpiritPos := OldPos;
NewTurnCount := OldTurnCount;
NewStepCount := OldStepCount + 1;//增加步数
case Dir of
mdUp : begin
Dec(NewPos.Y);
Inc(NewSpiritPos.Y);
if FMoveSpirit(OldSpiritPos, NewSpiritPos, SpiritTrace, OldPos) then
begin
NewTrace := OldTrace + SpiritTrace + 'N';
if (OldTrace <> '') and (OldTrace[Length(OldTrace)] <> 'N') then
Inc(NewTurnCount);
end
else
Exit;
end;
mdDown : begin
Inc(NewPos.Y);
Dec(NewSpiritPos.Y);
if FMoveSpirit(OldSpiritPos, NewSpiritPos, SpiritTrace, OldPos) then
begin
NewTrace := OldTrace + SpiritTrace + 'S';
if (OldTrace <> '') and (OldTrace[Length(OldTrace)] <> 'S') then
Inc(NewTurnCount);
end
else
Exit;
end;
mdLeft : begin
Dec(NewPos.X);
Inc(NewSpiritPos.X);
if FMoveSpirit(OldSpiritPos, NewSpiritPos, SpiritTrace, OldPos) then
begin
NewTrace := OldTrace + SpiritTrace + 'E';
if (OldTrace <> '') and (OldTrace[Length(OldTrace)] <> 'E') then
Inc(NewTurnCount);
end
else
Exit;
end;
mdRight : begin
Inc(NewPos.X);
Dec(NewSpiritPos.X);
if FMoveSpirit(OldSpiritPos, NewSpiritPos, SpiritTrace, OldPos) then
begin
NewTrace := OldTrace + SpiritTrace + 'W';
if (OldTrace <> '') and (OldTrace[Length(OldTrace)] <> 'W') then
Inc(NewTurnCount);
end
else
Exit;
end;
end;
if (NewPos.Y >= 0) and (NewPos.Y <= FRow - 1) and (NewPos.X >= 1) and (NewPos.X <= FColumn) //检查是不是出界
and not (FMap.Strings[NewPos.Y][NewPos.X] in ['#'])//有没有障碍物
then
begin
RaceBox := FMarks[NewPos.Y * FColumn + NewPos.X - 1] as TBox;
if RaceBox <> nil then
begin
if (RaceBox.FPos.X <> NewPos.X) or (RaceBox.FPos.Y <> NewPos.Y) then
Exit//如果竞争箱子已经不在格子上说明,新箱子已经落后
else
if RaceBox.FStepCount < NewStepCount then
Exit//比较步数,少者优胜
else
if RaceBox.FTurnCount < NewTurnCount then
Exit//比较转弯次数,少者优胜
else
if Length(RaceBox.FTrace) <= Length(NewTrace) then
Exit//比较全部步数,包括精灵的,少者优胜
else
begin
RaceBox.Active := False;
end;
end;
if (OldPos.X = FPos.X) and (OldPos.Y = FPos.Y) then
aBox := Self
else
aBox := TBox.Create(FObjectList, FRow, FColumn, FMap, FMarks, FTarget);
with aBoxdo
begin
FPos.X := NewPos.X;
FPos.Y := NewPos.Y;
FTurnCount := NewTurnCount;
FTrace := NewTrace;
FSpiritPos := OldPos;
FStepCount := NewStepCount;
end;
FMarks[NewPos.Y * FColumn + NewPos.X - 1] := aBox;//占领格子
Result := (aBox.FPos.X = FTarget.X) and (aBox.FPos.Y = FTarget.Y);
if Result then
Winner := aBox;//返回胜利者
end;
end;
begin
Result := False;
OldPos := FPos;
OldTrace := FTrace;
OldTurnCount := FTurnCount;
OldSpiritPos := SpiritPos;
OldStepCount := FStepCount;
//向四个方向移动,每次移动之前先判断箱子不是向后退,,
if ((OldTrace = '') or (OldTrace[Length(OldTrace)] <> 'S')) and FMove(mdUp) then
begin
Result := True;
Exit;
end;
if ((OldTrace = '') or (OldTrace[Length(OldTrace)] <> 'N')) and FMove(mdDown) then
begin
Result := True;
Exit;
end;
if ((OldTrace = '') or (OldTrace[Length(OldTrace)] <> 'W')) and FMove(mdLeft) then
begin
Result := True;
Exit;
end;
if ((OldTrace = '') or (OldTrace[Length(OldTrace)] <> 'E')) and FMove(mdRight) then
begin
Result := True;
end;
if (FPos.X = OldPos.X) and (FPos.Y = OldPos.Y) then
Active := False;
end;

function TBox.FMoveSpirit(FromPos, Target : TPoint;
var Trace: String;
BoxPos : TPoint): Boolean;
var
FObjectList : TList;
FMarks : TMoveObjects;
i : Integer;
aSpirit : TSpirit;
ActiveCount : Integer;
Winner : TMoveObject;
begin
//如果开始点和结束点一样,返回空字符串
if (FromPos.X = Target.X) and (FromPos.Y = Target.Y) then
begin
Result := True;
Trace := '';
Exit;
end;
FObjectList := TList.Create;
try
//初始化FMarks(格子数组,用来保存到达格子的精灵,来比较到达某个格子精灵的快慢)
SetLength(FMarks, FRow * FColumn);
for i := 0 to Length(FMarks) - 1do
FMarks := nil;
//创建第一个精灵
aSpirit := TSpirit.Create(FObjectList, FRow, FColumn, FMap, FMarks, Target, BoxPos);
with aSpiritdo
begin
FPos := FromPos;
end;
FMarks[FromPos.Y * FColumn + FromPos.X - 1] := aSpirit;
Winner := nil;
//下面循环每次让每一个精灵走一步,最快到达目的地的精灵就是优胜者
repeat
ActiveCount := 0;
for i := FObjectList.Count - 1do
wnto 0do
begin
aSpirit := FObjectList.Items;
if aSpirit.Active then
begin
Inc(ActiveCount);
if aSpirit.Move then
Winner := aSpirit.Winner;
end
else
FObjectList.Delete(i);
end;
until (Winner <> nil) or (ActiveCount = 0);
if Winner <> nil then
begin
Trace := Winner.FTrace;
Result := True;
end
else
Result := False;
finally
//释放资源
for i := 0 to FObjectList.Count - 1do
begin
aSpirit := FObjectList.Items;
aSpirit.Free;
end;
FObjectList.Free;
end;
end;

procedure TBox.SetSpiritPos(const Value: TPoint);
begin
FSpiritPos := Value;
end;

procedure TForm1.FShowAction(Trace: String);
procedure FSetPoint(P : TPoint;
C : Char);
var
S : String;
begin
S := Memo1.Lines.Strings[P.Y];
S[P.X] := C;
Memo1.Lines.Strings[P.Y] := S;
end;
var
i : Integer;
begin
Pos,
Target,
BoxPos : TPoint;
begin
begin
Pos.X := -1;
Target.X := -1;
BoxPos.X := -1;
for i := 0 to Memo1.Lines.Count -1do
begin
if begin
Pos.X <= 0 then
begin
begin
Pos.X := Pos('S', Memo1.Lines.Strings);
if begin
Pos.X > 0 then
begin
begin
Pos.Y := i;
end;
end;
if Target.X <= 0 then
begin
Target.X := Pos('T', Memo1.Lines.Strings);
if Target.X > 0 then
begin
Target.Y := i;
end;
end;
if BoxPos.X <= 0 then
begin
BoxPos.X := Pos('B', Memo1.Lines.Strings);
if BoxPos.X > 0 then
begin
BoxPos.Y := i;
end;
end;
end;
for i := 0 to Length(Trace)do
begin
case Trace of
'n' : begin
FSetPoint(begin
Pos, '.');
begin
Pos.Y := begin
Pos.Y - 1;
FSetPoint(begin
Pos, 'S');
end;
's' : begin
FSetPoint(begin
Pos, '.');
begin
Pos.Y := begin
Pos.Y + 1;
FSetPoint(begin
Pos, 'S');
end;
'e' : begin
FSetPoint(begin
Pos, '.');
begin
Pos.X := begin
Pos.X - 1;
FSetPoint(begin
Pos, 'S');
end;
'w' : begin
FSetPoint(begin
Pos, '.');
begin
Pos.X := begin
Pos.X + 1;
FSetPoint(begin
Pos, 'S');
end;
'N' : begin
FSetPoint(begin
Pos, '.');
begin
Pos := BoxPos;
FSetPoint(BoxPos, 'S');
BoxPos.Y := BoxPos.Y - 1;
FSetPoint(BoxPos, 'B');
end;
'S' : begin
FSetPoint(begin
Pos, '.');
begin
Pos := BoxPos;
FSetPoint(BoxPos, 'S');
BoxPos.Y := BoxPos.Y + 1;
FSetPoint(BoxPos, 'B');
end;
'E' : begin
FSetPoint(begin
Pos, '.');
begin
Pos := BoxPos;
FSetPoint(BoxPos, 'S');
BoxPos.X := BoxPos.X - 1;
FSetPoint(BoxPos, 'B');
end;
'W' : begin
FSetPoint(begin
Pos, '.');
begin
Pos := BoxPos;

FSetPoint(BoxPos, 'S');
BoxPos.X := BoxPos.X + 1;
FSetPoint(BoxPos, 'B');
end;
end;
Sleep(100);
end;
end;

function TForm1.FGetShortCut: String;
var
FObjectList : TList;
FMarks : TMoveObjects;
i : Integer;
begin
Pos,
Target,
BoxPos : TPoint;
FColumn : Integer;
aBox : TBox;
ActiveCount : Integer;
Winner : TMoveObject;
begin
//取得精灵、箱子、和目标的位置
begin
Pos.X := -1;
Target.X := -1;
BoxPos.X := -1;
for i := 0 to Memo1.Lines.Count -1do
begin
if begin
Pos.X <= 0 then
begin
begin
Pos.X := Pos('S', Memo1.Lines.Strings);
if begin
Pos.X > 0 then
begin
begin
Pos.Y := i;
end;
end;
if Target.X <= 0 then
begin
Target.X := Pos('T', Memo1.Lines.Strings);
if Target.X > 0 then
begin
Target.Y := i;
end;
end;
if BoxPos.X <= 0 then
begin
BoxPos.X := Pos('B', Memo1.Lines.Strings);
if BoxPos.X > 0 then
begin
BoxPos.Y := i;
end;
end;
end;
if (begin
Pos.X > 0) and (BoxPos.X > 0) and (Target.X > 0) then
begin
FObjectList := TList.Create;
try
FColumn := Length(Memo1.Lines.Strings[0]);
//初始化格子数组,用来存放到达格子的箱子,以便比较箱子的快慢
SetLength(FMarks, Memo1.Lines.Count * FColumn);
for i := 0 to Length(FMarks) - 1do
FMarks := nil;
aBox := TBox.Create(FObjectList, Memo1.Lines.Count, FColumn, Memo1.Lines, FMarks, Target);
with aBoxdo
begin
FPos := BoxPos;
SpiritPos := begin
Pos;
end;
FMarks[begin
Pos.Y * FColumn + begin
Pos.X - 1] := aBox;
Winner := nil;
//下面循环每次让每个箱子走一步,最快到达目的地的箱子走的路径就是最短路径
repeat
ActiveCount := 0;
for i := FObjectList.Count - 1do
wnto 0do
begin
aBox := FObjectList.Items;
if aBox.Active then
begin
Inc(ActiveCount);
if aBox.Move then
Winner := aBox.Winner;
end
else
FObjectList.Delete(i);//删除被淘汰的箱子
end;
until (Winner <> nil) or (ActiveCount = 0);
if Winner <> nil then
begin
Result := Winner.FTrace;
end
else
Result := '';
finally
//释放资源
for i := 0 to FObjectList.Count - 1do
begin
aBox := FObjectList.Items;
aBox.Free;
end;
FObjectList.Free;
end;
end;
end;

constructor TBox.Create(ObjectList: TList;
Row, Column: Integer;
Map: TStrings;
Marks: TMoveObjects;
Target: TPoint);
begin
inherited Create(ObjectList, Row, Column, Map, Marks, Target);
FStepCount := 0;
end;

end.
 
to Tassadar:
谢谢你的代码,我仔细看看。
to LeeChange:
呵呵,按照题目的意思,迷宫的规模不大。
 
规模不大就广度优先吧,已经有代码了,我就不再写了.
 
精灵和精灵会不会相互阻挡啊?
 
to AI:楼主已确认只有一个精灵了.
 
那不就只是简单的广搜?
bty:iknowabc朋友给我发消息,但我不知道该怎么发,就在这里回答了
我没有见到过金牌之路题解的下载,不过这本书到处都有卖的
 

Similar threads

S
回复
0
查看
3K
SUNSTONE的Delphi笔记
S
S
回复
0
查看
2K
SUNSTONE的Delphi笔记
S
后退
顶部