[url=http://eastsun.javaeye.com/blog/124477]本题JAVA及C++代码下载[/url]
原题:在8*8的棋盘上分布着n个骑士,他们想约在某一格中聚会.骑士每天可以像国际象棋中的马那样移动一次.请你计算n个骑士的最早聚会地点和要走多少天.要求尽早聚会,且n个人走的总步数最少,先到的骑士可以不再移动等待其它骑士.
输入:从键盘输入n(0<n<=64),然后依次输入n个骑士的初始位置xi,yi(0<=xi,yi<=7)
输出:以空格分隔的三个整数,分别为聚会点的x,y值,以及要走多少天
对问题的分析:为了解决这个问题,显然我们得想办法能计算出骑士从棋盘上的某个格子到其它格子需要的最少步数.
(另一方面,很容易可以证明骑士可以从棋盘上任意一个格子到达另一个格子)当然,最直接的想法是用搜索,挨个试一遍就OK了.
这也是种方法,如果加上动态规划,也是可行的算法.不过这里我没用这种方法,而是采用图论算法中已有的用于计算一个图中任意两点最短路径的经典算法--Floyd-Warshall算法.
我们可以将棋盘的64个格子看成64个节点,其中两个节点中存在一条边相连当且仅当骑士可以直接从其中的一个节点跳到另一个节点.这样骑士从棋盘某点到另一点需要移动的最少次数就转化为计算这个图中两个节点的最短路问题.至于Floyd-Warshall算法我就不多说了,随便baidu一下就知道.
剩下的问题就好办了,下面是我写的JAVA代码:
/**
&#Main.java
<程序员>2007第九期算法擂台之JAVA代码
注: 该问题的解未必唯一,但根据问题需要,只给出一个解
另: 经测试,运行1000次new Knight()大约耗时6000毫秒
@author Eastsun
@version 1.0 2007/9/3
*/
import java.util.Scanner;
public class Main{
public static void main(String[] args){
Scanner scan =new Scanner(System.in);
int n =scan.nextInt();
int[][] pos =new int[n][2];
for(int index =0;index<n;index ++){
pos[index][0] =scan.nextInt();
pos[index][1] =scan.nextInt();
}
ResultInf result =new Knight().solve(pos);
System.out.print(result.x +\" \" +result.y +\" \" +result.days);
}
}
class Knight{
private static final int DEFAULT_SIZE = 8;
private int boardSize;
//dis[i][j]表示骑士从棋盘上位置i到j所需的最少步数,其中位置i对于棋盘坐标(i%boardSize,i/boardSize)
private int[][] dist;
/**
Knight的构造函数
@param boardSize 棋盘的大小,默认值为8
*/
public Knight(int boardSize){
this.boardSize =boardSize;
initDist();
}
public Knight(){
this(DEFAULT_SIZE);
}
/**
根据骑士的位置求解
@param pos 一个int[][2]数组,第k个骑士的位置为(pos[k][0],pos[k][1])
@return result 该问题的解,注意:当result.days ==Integer.MAX_VALUE的时候说明这些骑士永远不可能聚在一起
*/
public ResultInf solve(int[][] pos){
if(pos ==null||pos.length ==0|| pos[0].length !=2) throw new IllegalArgumentException();
int location =0,days =Integer.MAX_VALUE,allDays =0;
for(int n =0;n<boardSize*boardSize;n ++){
int max =0,sum =0;
for(int index =0;index<pos.length;index ++){
int dis = dist[n][pos[index][0]+pos[index][1]*boardSize];
sum += dis;
if(max<dis) max =dis;
}
if(days>max ||(days==max&&allDays>sum)){
days =max;
allDays =sum;
location =n;
}
}
return new ResultInf(location%boardSize,location/boardSize,days);
}
/**
使用Floyd-Warshall算法计算dist的值,时间复杂度O(boardSize^6)
*/
private void initDist(){
int count =boardSize*boardSize;
dist =new int[count][count];
for(int n =0;n<count;n++)
for(int m =0;m<count;m++)
if(m!=n) dist[m][n] = isConnect(m%boardSize,m/boardSize,n%boardSize,n/boardSize)? 1: Integer.MAX_VALUE;
for(int k =0;k<count;k++)
for(int n =0;n<count;n++)
for(int m =0;m<count;m++)
if(dist[n][k]<Integer.MAX_VALUE&&dist[k][m]<Integer.MAX_VALUE&&
dist[n][k]+dist[k][m] <dist[n][m])
dist[n][m] =dist[n][k] +dist[k][m];
}
/**
判断(x1,y1)与(x2,y2)是否直接可到
*/
private boolean isConnect(int x1,int y1,int x2,int y2){
return (x1!=x2&&y1!=y2&&Math.abs(x1-x2)+Math.abs(y1-y2)==3);
}
}
/**
一个用于保存求解信息的类
*/
class ResultInf{
public int x,y,days;
public ResultInf(int x,int y,int days){
this.x =x;
this.y =y;
this.days =days;
}
}