马拦过河卒(Java实现)

一、问题描述:

1、在部分的象棋棋盘(都是方格,大小可从键盘输入)中,假设卒只能向下或者向右移动,且卒在原点A(0,0)位置

2、棋盘中有马(初始位置由键盘输入),马可向任意方向移动一步,包括其初始位置都是卒的禁区

3、约定B点不同于A点,且不同于马的初始位置;

4、求卒移动到棋盘最大的终点位置B(m,n)的所有路径数;

如图所示:假设马在其中标注的中心黑点处,则其所有标注的黑点都为卒的禁区(玩过象棋的朋友就会知道这是什么意思)


解题方法同 卒的移动问题(JAVA)

只不过在一步一步遍历时,多了一些限制条件(有些点无法经过)。


代码改动说明:

1、增加一个Horse类,它继承了Chessman这个棋子类,它可向8个方向移动一步,获取下一步位置的方式也有所改变;

2、

1)Pawn(卒)类里增加一个违禁位置集合,当在移动过程中遇到集合中的点时这条路径就结束了(其它方向上的路径继续);

2)Pawn复写了父类Chessman的判定越界方法,即增加了违禁位置的判断;

3)ChessGame中增加了Horse的实例,并在马移动一步后,将其起始位置即所有下一步的位置设置到卒的违禁位置集合中,其它代码基本上没有变化;

4、增加马的输入参数即校验;


代码如下:

Position.java

/**
 * 坐标位置对象
 * 
 * @author dobuy
 * @time 2013-5-12
 */
public class Position
{
	private int x;

	private int y;

	public Position(int x, int y)
	{
		super();
		this.x = x;
		this.y = y;
	}

	/**
	 * 获取偏移后的位置
	 * 
	 * @param offset 偏移量
	 * @return
	 */
	public Position offset(Position offset)
	{
		return new Position(getX() + offset.getX(), getY() + offset.getY());
	}

	/**
	 * 偏移量的X,Y坐标交换位置
	 * 
	 * @return
	 */
	public Position reversal()
	{
		return new Position(getY(), getX());
	}

	public int getX()
	{
		return x;
	}

	public void setX(int x)
	{
		this.x = x;
	}

	public int getY()
	{
		return y;
	}

	public void setY(int y)
	{
		this.y = y;
	}

	@Override
	public int hashCode()
	{
		final int prime = 31;
		int result = 1;
		result = prime * result + x;
		result = prime * result + y;
		return result;
	}

	@Override
	public boolean equals(Object obj)
	{
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Position other = (Position) obj;
		if (x != other.x)
			return false;
		if (y != other.y)
			return false;
		return true;
	}

	@Override
	public String toString()
	{
		return "Position [x=" + x + ", y=" + y + "]";
	}
}
Chessboard.java

/**
 * 棋盘(棋子的地图)
 * 
 * @author dobuy
 * @time 2013-5-12
 */
public class Chessboard
{
	/**
	 * 棋盘的最小边界点(原点)
	 */
	private Position origonPosition;

	/**
	 * 棋盘的最大边界点
	 */
	private Position edgePosition;

	public Chessboard(Position edgePosition)
	{
		this.origonPosition = new Position(0, 0);
		this.edgePosition = edgePosition;
	}

	/**
	 * 当前位置在棋盘中是否越界
	 * 
	 * @return
	 */
	public boolean isOverEdge(Position currentPosition)
	{
		if (currentPosition.getX() < getOrigonPosition().getX()
				|| currentPosition.getY() < getOrigonPosition().getY()
				|| currentPosition.getX() > getEdgePosition().getX()
				|| currentPosition.getY() > getEdgePosition().getY())
		{
			return true;
		}
		return false;
	}

	public Position getEdgePosition()
	{
		return edgePosition;
	}

	private Position getOrigonPosition()
	{
		return origonPosition;
	}
}
Chessman.java

import java.util.ArrayList;
import java.util.List;

/**
 * 棋子类,描述棋子的位置属性及移动功能
 * 
 * @author dobuy
 * @time 2013-5-12
 */
public abstract class Chessman
{
	/**
	 * 棋子拥有一张棋盘地图
	 */
	private Chessboard chessMap;

	/**
	 * 起始位置
	 */
	private Position origonPos;

	/**
	 * 移动的步伐向量,如卒的当前位置为(x,y),移动向量为(0,1),移动一次时,
	 * 既可以表示向右移动一格(x+1,y+0),也可以表示向下移动一格(x+0,y+1) 即:约定移动的步伐向量不分横纵坐标
	 */
	private Position step;

	public Chessman(Position origonPos, Position step)
	{
		this.origonPos = origonPos;
		this.step = step;
	}

	public List<Position> moveNext()
	{
		return moveNext(getOrigonPos());
	}

	/**
	 * 从当前位置移动一步后的所有可能位置
	 * 
	 * @param currentPosition 当前位置
	 * @return
	 */
	public List<Position> moveNext(Position currentPosition)
	{
		return getNextPositionByStep(currentPosition);
	}

	public void setChessMap(Chessboard chessMap)
	{
		this.chessMap = chessMap;
	}

	public Position getOrigonPos()
	{
		return origonPos;
	}

	public Position getStep()
	{
		return step;
	}

	public Chessboard getChessMap()
	{
		return chessMap;
	}

	public boolean isOverEdge()
	{
		return isOverEdge(getOrigonPos());
	}

	/**
	 * 棋子是否越界,子类可扩展
	 * 
	 * @param currentPosition 棋子当前位置
	 * @return
	 */
	protected boolean isOverEdge(Position currentPosition)
	{
		return getChessMap().isOverEdge(currentPosition);
	}

	/**
	 * 棋子从起点一直移动到终点,并返回所有可能的路径总数
	 * 
	 */
	protected abstract long move();

	/**
	 * 棋子根据规则获取下一步的所有位置(可扩展,目前只有向右和向下)
	 * 
	 * @param currentPosition 棋子的当前位置
	 * @return
	 */
	protected List<Position> getNextPositionByStep(Position currentPosition)
	{
		return getNextPositionByStep(currentPosition, getStep());
	}

	protected List<Position> getNextPositionByStep(Position currentPosition,
			Position step)
	{
		List<Position> nextPositions = new ArrayList<Position>();

		Position nextPosition = currentPosition.offset(step);
		addNextPosition(nextPositions, nextPosition);

		nextPosition = currentPosition.offset(step.reversal());
		addNextPosition(nextPositions, nextPosition);

		return nextPositions;
	}

	/**
	 * 向下一步集合中添加一个位置,越界则不添加
	 * 
	 * @param nextPositions
	 * @param nextPosition
	 */
	protected void addNextPosition(List<Position> nextPositions,
			Position nextPosition)
	{
		if (!isOverEdge(nextPosition))
		{
			nextPositions.add(nextPosition);
		}
	}

	/**
	 * 获取棋盘的最大边界值
	 * 
	 */
	protected Position getEdgePosition()
	{
		return getChessMap().getEdgePosition();
	}
}
Pawn.java

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * 卒
 * 
 * @author dobuy
 * @time 2013-5-12
 */
public class Pawn extends Chessman
{
	Map<Position, Long> positionMap;

	private List<Position> illegalPositions;

	public Pawn(Position origonPos, Position step)
	{
		super(origonPos, step);
		positionMap = new HashMap<Position, Long>();
	}

	/*
	 * (non-Javadoc)
	 */
	@Override
	protected long move()
	{
		getPositionMap().put(getOrigonPos(), 1L);
		try
		{
			countWays();
		}
		catch (StackOverflowError e)
		{
			return -1;
		}
		Position edgePosition = getChessMap().getEdgePosition();

		if (getPositionMap().containsKey(edgePosition))
		{
			return getPositionMap().get(edgePosition);
		}

		return 0;
	}

	public void setIllegalPositions(List<Position> illegalPositions)
	{
		this.illegalPositions = illegalPositions;
	}

	@Override
	protected boolean isOverEdge(Position currentPosition)
	{
		boolean isOverEdge = super.isOverEdge(currentPosition);

		// 本次实现的原则是先移动,再判断马的范围:
		// 1、先判定是否到达终点,再判定是否在马的范围;
		// 2、即使起点在马的覆盖范围,卒先移动一步就超出这个范围了
		if (currentPosition.equals(getOrigonPos())
				|| currentPosition.equals(getEdgePosition()))
		{
			return isOverEdge;
		}
		return getIllegalPositions().contains(currentPosition) || isOverEdge;
	}

	/**
	 * <pre>
	 * 递归计算卒的下一步位置的经过次数
	 * 1、第一次取(0,0)点的次数;
	 * 2、经过(0,0)点的次数也等于经过(0,1)、(1,0)的次数之和;
	 * 3、经过(0,1)点的次数等于经过(1,1)、(0,2)的次数之和,同理(1,0)=(1,1)+(2,0)
	 * ……
	 * 经过如上可以观察得出结论:
	 * 经过当前点X的次数=X所有下一点的次数之和,且所有下一点的次数和X的次数相同
	 * 
	 * 算法:
	 * 1、保存经过(0,0)点的次数为1,并保存至集合M中;
	 * 2、遍历集合M中的所有点,假设当前点为X,遍历X的所有合法的下一点(不合法直接丢弃),
	 * 1)若下一点N不在M中,则保存N至M中,其次数为X的次数;
	 * 2)若N已经在集合M中,则N的次数为N的次数与X的次数之和;
	 * 3)遍历完X的所有子节点后,从M中删除X节点;
	 * 3、重复2,直至结合中为空或者只有终点
	 * </pre>
	 */
	private void countWays() throws StackOverflowError
	{
		if (getPositionMap().isEmpty()
				|| getPositionMap()
						.containsKey(getChessMap().getEdgePosition()))
		{
			return;
		}

		// 为了避免
		Map<Position, Long> currentPositionMaps = new HashMap<Position, Long>();
		currentPositionMaps.putAll(getPositionMap());
		Iterator<Position> nextPosIterator = currentPositionMaps.keySet()
				.iterator();

		Position currentPosition = null;
		List<Position> nextPositions = null;
		while (nextPosIterator.hasNext())
		{
			currentPosition = nextPosIterator.next();

			nextPositions = moveNext(currentPosition);
			for (Position nextPosition : nextPositions)
			{
				addNextPosition(currentPosition, nextPosition);
			}
			getPositionMap().remove(currentPosition);
		}
		countWays();
	}

	/**
	 * 把当前位置(C点)的下一步位置(N点)加入集合中,N点的经过次数为C点与N点的次数之和(N点不在集合中时,次数为C点次数)
	 * 
	 */
	private void addNextPosition(Position currentPosition, Position nextPosition)
			throws StackOverflowError
	{
		// 不合法位置,丢弃
		if (isOverEdge(nextPosition))
		{
			return;
		}

		long count = getPositionMap().get(currentPosition);
		if (getPositionMap().containsKey(nextPosition))
		{
			long currentCount = getPositionMap().remove(nextPosition);

			if (isAddResultSizeOverflow(count, currentCount))
			{
				throw new StackOverflowError(
						"It's too big number to count ways!");
			}
			count = count + currentCount;
		}
		getPositionMap().put(nextPosition, count);
	}

	/**
	 * 判断2个long型变量之和是否超出Long的范围
	 * 
	 * isAddResultSizeOverflow(这里用一句话描述这个方法的作用)
	 */
	private boolean isAddResultSizeOverflow(long count1, long count2)
	{
		BigDecimal countDecimal1 = BigDecimal.valueOf(count1);
		BigDecimal countDecimal2 = BigDecimal.valueOf(count2);

		BigDecimal sum = countDecimal1.add(countDecimal2);

		BigDecimal max = BigDecimal.valueOf(Long.MAX_VALUE);
		return sum.compareTo(max) >= 0;
	}

	private Map<Position, Long> getPositionMap()
	{
		return positionMap;
	}

	private List<Position> getIllegalPositions()
	{
		return illegalPositions;
	}
}
Horse.java

import java.util.List;

/**
 * 
 * 类名称:Horse 类描述: 创建人:dobuy
 * 
 */
public class Horse extends Chessman
{
	/**
	 * 创建一个新的实例 Horse.
	 * 
	 * @param origonPos
	 * @param step
	 */
	public Horse(Position origonPos, Position step)
	{
		super(origonPos, step);
	}

	/**
	 * 获取下一步的8个方向上的位置
	 */
	@Override
	protected List<Position> getNextPositionByStep(Position currentPosition)
	{
		// 只有向右和向下2个方向(偏移量分别为(x,y)、(y,x),x,y互换的偏移量用一个点表示),扩展其它6个方向
		List<Position> nextPositions = super
				.getNextPositionByStep(currentPosition);

		// 另6个方向可用如下三个偏移量表示
		Position[] steps = new Position[] {
				new Position(-getStep().getX(), getStep().getY()),
				new Position(-getStep().getX(), -getStep().getY()),
				new Position(getStep().getX(), -getStep().getY()) };

		List<Position> nextPossiblePositions = null;

		for (Position step : steps)
		{
			nextPossiblePositions = getNextPositionByStep(currentPosition, step);
			addPossiblePositions(nextPositions, nextPossiblePositions);
		}

		return nextPositions;
	}

	@Override
	protected long move()
	{
		return 0;
	}

	/**
	 * 添加下一步可能的几个点
	 * 
	 */
	private void addPossiblePositions(List<Position> nextPositions,
			List<Position> nextPossiblePositions)
	{
		for (Position nextPossiblePosition : nextPossiblePositions)
		{
			addNextPosition(nextPositions, nextPossiblePosition);
		}
	}
}
ChessGame.java

import java.util.List;

/**
 * 中国象棋
 * 
 * @author dobuy
 * @time 2013-5-12
 */
public class ChineseChess
{
	/**
	 * 卒
	 */
	private Pawn pawn;

	/**
	 * 马
	 */
	private Horse horse;

	/**
	 * 地图
	 */
	private Chessboard chessboard;

	private static final int ERROR = -1;

	/**
	 * 启动入口
	 * 
	 * @param edgePoint 最大边界的坐标数组
	 * @return
	 */
	public long startGame(int[] edgePoint, int[] horsePosition)
	{
		if (isIllegalParams(edgePoint) || isIllegalParams(horsePosition))
		{
			return ERROR;
		}

		Position edgePosition = new Position(edgePoint[0], edgePoint[1]);

		Position horseOriginPos = new Position(horsePosition[0],
				horsePosition[1]);

		return startGame(edgePosition, horseOriginPos);
	}

	/**
	 * 真正的入口
	 * 
	 * @return
	 */
	private long startGame(Position edgePosition, Position horseOriginPos)
	{
		Position originPos = new Position(0, 0);
		// 约定起点、终点、马的初始点,3者不能相等
		if (edgePosition.equals(horseOriginPos)
				|| edgePosition.equals(originPos)
				|| horseOriginPos.equals(originPos))
		{
			return ERROR;
		}

		init(edgePosition, horseOriginPos);

		if (horse.isOverEdge())
		{
			return ERROR;
		}
		return getPawn().move();
	}

	private void init(Position edgePosition, Position horseOriginPos)
	{
		this.chessboard = new Chessboard(edgePosition);
		this.pawn = new Pawn(new Position(0, 0), new Position(0, 1));
		horse = new Horse(horseOriginPos, new Position(1, 2));
		getPawn().setChessMap(getChessboard());
		horse.setChessMap(getChessboard());

		List<Position> horsePositions = horse.moveNext();
		horsePositions.add(horseOriginPos);

		getPawn().setIllegalPositions(horsePositions);
	}

	private Pawn getPawn()
	{
		return pawn;
	}

	private Chessboard getChessboard()
	{
		return chessboard;
	}

	private boolean isIllegalParams(int[] position)
	{
		if (position == null || position.length != 2 || position[0] < 0
				|| position[1] < 0)
		{
			return true;
		}
		return false;
	}
}
 单元测试类:

import static org.junit.Assert.assertEquals;

import org.junit.Before;
import org.junit.Test;

/**
 * 
 * 类名称:ChineseChessTest 类描述: 创建人:dobuy
 * 
 */
public class ChineseChessTest
{

	private ChineseChess chineseChess;

	private int[] edgePoint;

	private int[] horsePoint;

	@Before
	public void before()
	{
		chineseChess = new ChineseChess();
	}

	/**
	 * Testcase1:正常流程
	 * 
	 */
	@Test
	public void testStartGame1()
	{
		edgePoint = new int[] { 6, 6 };
		horsePoint = new int[] { 2, 1 };
		assertEquals(chineseChess.startGame(edgePoint, horsePoint), 36);
	}

	/**
	 * Testcase2:正常流程(大数据)
	 * 
	 */
	@Test
	public void testStartGame2()
	{
		edgePoint = new int[] { 30, 30 };
		horsePoint = new int[] { 30, 0 };
		assertEquals(chineseChess.startGame(edgePoint, horsePoint),
				118264581564836135L);
	}

	/**
	 * Testcase2:正常流程(临界值:无法到达终点)
	 * 
	 */
	@Test
	public void testStartGame3()
	{
		edgePoint = new int[] { 30, 30 };
		horsePoint = new int[] { 28, 28 };
		assertEquals(chineseChess.startGame(edgePoint, horsePoint), 0);
	}

	/**
	 * Testcase4:异常流程:参数越界非法
	 */
	@Test
	public void testStartGame4()
	{
		edgePoint = new int[] { -1, 2 };
		horsePoint = new int[] { 2, 2 };
		assertEquals(chineseChess.startGame(edgePoint, horsePoint), -1);
	}

	/**
	 * Testcase5:异常流程:参数位数非法
	 */
	@Test
	public void testStartGame5()
	{
		edgePoint = new int[] { 2 };
		horsePoint = new int[] { 2, 2 };
		assertEquals(chineseChess.startGame(edgePoint, horsePoint), -1);
	}

	/**
	 * Testcase6:异常流程:大数据越界
	 */
	@Test
	public void testStartGame6()
	{
		edgePoint = new int[] { 50, 50 };
		horsePoint = new int[] { 2, 20 };
		assertEquals(chineseChess.startGame(edgePoint, horsePoint), -1);
	}

	/**
	 * Testcase7:异常流程:马越界
	 */
	@Test
	public void testStartGame7()
	{
		edgePoint = new int[] { 50, 50 };
		horsePoint = new int[] { 2, 60 };
		assertEquals(chineseChess.startGame(edgePoint, horsePoint), -1);
	}
}






  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值