POJ1178

这是一道DP题,我写的时候也是尽量往DP了想,最后使用了一种蹩脚的类似DP又不太像是DP的方法写了出来

求最小的移动步数,先得求出一个已知骑士和国王的位置,求出其到其他位置的最小移动步数

国王的位置已知的话,最小步数就是两点之间的曼哈顿距离

骑士位置已知的话,用广度优先搜索就可以求出最小步数,将骑士的位置用1-64编码,将编号为K的骑士移动到(i,j)的点的最小步数储存在数组step[i][j][K],每次计算保存下来,这样避免了重复搜索。

骑士移动的规则用一个8*2的数组表示,可以用循环来代替多个if判断。

既然是一道DP题,本题的初始状态就是只有一个国王,此时dp[i][j][0]表示国王到(i,j)点所要的最小步数

加上一个骑士k之后,自然dp[i][j][1]=dp[m][n][0]+step[m][n][k]+step[i][j][T]

如何理解上式呢

dp[i][j][1]储存的是一个骑士的情况下最终在(i,j)处结束游戏所要的最小步数,这个步数等于骑士和国王在(m,n)集合然后一起走到(i,j),然后对(m,n)遍历后得到的最小值。

那岂不是求出了很多不必要的东西,既然骑士和国王都走到了一起,不就步数最少了吗,还要移动干什么呢?

其实,这是为了下一层做准备,因为最关键的就是,国王和骑士到(i,j)点的最短步数,并不是他们先以最短的步数走到一起,再以最短的步数走到(i,j)这两个步数之和。

当我们计算下一层的时候,游戏就不一定在这次国王和骑士相遇的地方停止了,可能他们相遇之后还要一起走到某个位置和其他的骑士会和,才有最小的步数。

接着,对于本次读取的骑士K,dp[i][j][n]=min(step[i][j][K]+dp[i][j][deep-1],nowMatrix[i][j]+preKnight[i][j]);

上式可以这么理解,多读取一个骑士无非两种情况,国王仍然和之前的骑士组合,或者国王和新的骑士组合

如果国王和之前的骑士组合,那问题就简单了,只要上一层中所有棋子到各个点的最小步数+骑士K到各个点最小步数这个和中的最小值就行了

如果国王和新的骑士组合,就很麻烦了,我们要类似第二步重新计算出国王和新骑士一起到各个点的最小步数(即nowMatrix[i][j]),然后和之前所有骑士一起到各个点的最小步数的矩阵(即preKnight[i][j])求个和,其中找到最小值即可。

在这个过程中,我始终记录了K之前所有骑士一起到各个点的最小步数,避免了重复运算。

一遍就通过了,260K 0MS我还是很满意的,虽然代码又长又傻...

网上有很多其他好的方法,比如用FLoyd算法求距离,其实感觉也是很好的算法,这个时候把棋盘从二维转化为一维,用一个64*64的矩阵记录两两点之间的距离就可以了,稍微麻烦的就是对骑士的矩阵初始化的时候,不过感觉挺好的。

代码如下:

#include<stdio.h>
#include<memory.h>
#include<queue>
#include<math.h>
using namespace std;
#define MAX 9
int step[MAX][MAX][65];
int stepMark[65];
int king[MAX][MAX];
int dp[MAX][MAX][64];
int nowMatrix[MAX][MAX];
int preKnight[MAX][MAX];
queue<int> q;
int knightMove[8][2]={-1,-2,-2,-1,-2,1,-1,2,1,2,2,1,2,-1,1,-2};
inline bool realPos(int x,int y){
	if(x<=8&&x>=1&&y<=8&&y>=1){return true;}
	else{return false;}
}
void calStep(int x,int y){
	int num=(x-1)*8+y;
	if(stepMark[(x-1)*8+y]){return;}
	q.push((x-1)*8+y);
	stepMark[(x-1)*8+y]=1;
	step[x][y][(x-1)*8+y]=0;
	int nowX,nowY,nowQ,nextX,nextY,Q;
	while(!q.empty()){
		Q=q.front();
		q.pop();
		nowX=(Q-1)/8+1;
		nowY=Q-(nowX-1)*8;
		for(int i=0;i<=7;i++){
			nextX=nowX+knightMove[i][0];
			nextY=nowY+knightMove[i][1];
			if(realPos(nextX,nextY)){
				if(step[nextX][nextY][num]==-1){
					step[nextX][nextY][num]=step[nowX][nowY][num]+1;
					q.push((nextX-1)*8+nextY);
				}
			}
		}
	}
}
int transX(char t){
	return '9'-t;
}
int transY(char t){
	return t-'A'+1;
}
int main(){
	memset(step,-1,sizeof(step));
	memset(stepMark,0,sizeof(stepMark));
	memset(preKnight,0,sizeof(preKnight));
	char str[129];
	scanf("%s",&str);
	char X,Y;
	int count=0;
	int x,y;
	int minN=9999999;
	while(str[count]!='\0'){
		minN=9999999;
		X=str[count++];
		Y=str[count++];
		x=transX(Y);
		y=transY(X);
		if(count==2){
			for(int i=1;i<=MAX-1;i++){
				for(int j=1;j<=MAX-1;j++){
					king[i][j]=abs(x-i)+abs(y-j);
					dp[i][j][0]=king[i][j];
				}
			}
		}
		else{
			int deep=count/2-1;
			calStep(x,y);
			int num=(x-1)*8+y;
			for(int m=1;m<=MAX-1;m++){
				for(int n=1;n<=MAX-1;n++){
					calStep(m,n);
					int num2=(m-1)*8+n;
					for(int i=1;i<=MAX-1;i++){
						for(int j=1;j<=MAX-1;j++){
							if(m==1&&n==1){
								nowMatrix[i][j]=step[i][j][num2]+king[m][n]+step[m][n][num];
							}
							else{
								if(nowMatrix[i][j]>step[i][j][num2]+king[m][n]+step[m][n][num]){
									nowMatrix[i][j]=step[i][j][num2]+king[m][n]+step[m][n][num];
								}
							}
						}
					}
				}
			} 
			for(int i=1;i<=MAX-1;i++){
				for(int j=1;j<=MAX-1;j++){
					if(count==4){
						dp[i][j][deep]=nowMatrix[i][j];
					}
					else{
						dp[i][j][deep]=min(step[i][j][num]+dp[i][j][deep-1],nowMatrix[i][j]+preKnight[i][j]);
					}
					if(dp[i][j][deep]<minN){
						minN=dp[i][j][deep];
					}
				}
			}
			for(int i=1;i<=MAX-1;i++){
				for(int j=1;j<=MAX-1;j++){
					preKnight[i][j]=preKnight[i][j]+step[i][j][num];
				}
			}
		}	
	}
	if(count==2){printf("0");}
	else{printf("%d",minN);}
} 


猜你喜欢

转载自blog.csdn.net/luo3300612/article/details/78242288