Educational Codeforces Round 52: D. Three Pieces(记忆化搜索)

 

D. Three Pieces

 

题意:

给你一n*n的矩阵,每个格子都有一个数字且所有数字构成一个1~n²的全排列,一开始你的棋子在编号为1的点上,之后你要依次到达编号为2的点、编号为3的点…… 编号为n²的点,移动规则如下

  • 你有三种不同的棋子:①马(走日字)②皇后(到达同一斜线的任何地方)③车(到达同一行同一列的任何任何地方)
  • 你可以任选一种棋子作为开始
  • 可以花1点代价更换当前棋子
  • 可以花1点代价按照当前棋子规则移动一步
  • 从编号为i的格子到达编号为i+1的格子的过程中,可以经过任何其他格子,但是不能走出边界

求出最小代价,以及最小代价下最少换多少次棋子(尽可能少换棋子)

 

思路:

提供一个O(n²m)复杂度的方法,不需要BFS,n=100都能轻松解决,其中m为换棋子的次数(当n=10时m不会超过15,非常小)

其实依赖于另一个问题:

给你一个n*n的棋盘(n≥3),按马的走法从(x,y)走到(x', y')至少需要多少步,中途不能离开棋盘

代码如下(其中x, y是起点,ex, ey是终点):

int Jud(int x, int y, int ex, int ey)
{
	if((Check(x, y) || Check(ex, ey)) && abs(ex-x)==1 && abs(ey-y)==1)
	{
		if(n==3)
			return 22222222;
		return 4;
	}
	else if(n==4 && Check(x, y) && Check(ex, ey) && abs(ex-x)+abs(ey-y)==3)
		return 5;
	else if(n==3 && (abs(ex-x)==0 && abs(ey-y)==2 || abs(ex-x)==2 && abs(ey-y)==0) && !Check(x, y) && !Check(ex, ey))
		return 4;
	else
	{
		x = abs(x-ex), y = abs(y-ey);
		if(x<y)
			swap(x, y);
		if(x==1 && y==0)
			return 3;
		if(x==2 && y==2)
			return 4;
		if(x-y<y)
			return x-y-2*floor((x-2*y)/3.0);
		else
			return x-y-2*floor((x-2*y)/4.0);
	}
}

 

是的马的最短路问题不需要BFS, 是可以O(1)解决的, 而显然车和象很轻松的也能O(1)解决(注意象有永远到不了的格点)考虑dp[x][y][z]表示当前在编号为x的点上,已经换了y次棋子, 且当前棋子是马/皇后/车的最小代价, 然后就是简单的记忆化搜索了

 

但是!!这题有个坑,没注意就会各种挂终测,有一种很特殊的情况例如从(1, 1) 走到(9, 8),可以直接换车2步到达,也可以先按马走到(3, 2), 换象之后一步到达(9, 8),所以还需要判一下如果当前是马是否能一步后换象直接到达下一个目标点从而少换一次车!相反同理

 

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<map>
#include<string>
#include<math.h>
#include<queue>
#include<stack>
#include<stdlib.h>
#include<iostream>
using namespace std;
#define LL long long
#define mod 1000000007
typedef struct Res
{
	int x;
	int y;
}Res;
Res s[200005];
int n, dp[105][18][3], dir[8][2] = {1,-2,-1,2,-1,-2,1,2,2,-1,2,1,-2,-1,-2,1};
int Check(int x, int y)
{
	if(x==1 && y==1 || x==1 && y==n || x==n && y==1 || x==n && y==n)
		return 1;
	return 0;
}
int MtoX(int x, int y, int ex, int ey)
{
	int i, dx, dy;
	for(i=0;i<=7;i++)
	{
		dx = x+dir[i][0];
		dy = y+dir[i][1];
		if(dx>=1 && dx<=n && dy>=1 && dy<=n && abs(ex-dx)==abs(ey-dy))
			return 1;
	}
	return 0;
}
int XtoM(int x, int y, int ex, int ey)
{
	int i, dx, dy;
	for(i=0;i<=7;i++)
	{
		dx = ex+dir[i][0];
		dy = ey+dir[i][1];
		if(dx>=1 && dx<=n && dy>=1 && dy<=n && abs(x-dx)==abs(y-dy))
			return 1;
	}
	return 0;
}
int Jud(int x, int y, int ex, int ey)
{
	if((Check(x, y) || Check(ex, ey)) && abs(ex-x)==1 && abs(ey-y)==1)
	{
		if(n==3)
			return 22222222;
		return 4;
	}
	else if(n==4 && Check(x, y) && Check(ex, ey) && abs(ex-x)+abs(ey-y)==3)
		return 5;
	else if(n==3 && (abs(ex-x)==0 && abs(ey-y)==2 || abs(ex-x)==2 && abs(ey-y)==0) && !Check(x, y) && !Check(ex, ey))
		return 4;
	else
	{
		x = abs(x-ex), y = abs(y-ey);
		if(x<y)
			swap(x, y);
		if(x==1 && y==0)
			return 3;
		if(x==2 && y==2)
			return 4;
		if(x-y<y)
			return x-y-2*floor((x-2*y)/3.0);
		else
			return x-y-2*floor((x-2*y)/4.0);
	}
}
int Sech(int id, int p, int now)
{
	int ex, ey, x, y;
	if(dp[id][p][now]!=-1)
		return dp[id][p][now];
	if(id==1)
	{
		dp[id][p][now] = p;
		return p;
	}
	dp[id][p][now] = 22222222;
	if(p!=0)
		dp[id][p][now] = min(Sech(id, p-1, 0)+1, min(Sech(id, p-1, 1)+1, Sech(id, p-1, 2)+1));
	ex = s[id].x, ey = s[id].y;
	x = s[id-1].x, y = s[id-1].y;
	if(now==2)
	{
		if(ex==x || ey==y)
			dp[id][p][2] = min(dp[id][p][2], Sech(id-1, p, 2)+1);
		else
			dp[id][p][2] = min(dp[id][p][2], Sech(id-1, p, 2)+2);
	}
	if(now==1)
	{
		if(abs(ex-x)==abs(ey-y))
			dp[id][p][1] = min(dp[id][p][1], Sech(id-1, p, 1)+1);
		else if((ex+ey)%2==(x+y)%2)
			dp[id][p][1] = min(dp[id][p][1], Sech(id-1, p, 1)+2);
		else if(MtoX(x, y, ex, ey) && p!=0)
			dp[id][p][1] = min(dp[id][p][1], Sech(id-1, p-1, 0)+3);
	}
	if(now==0)
	{
		if(XtoM(x, y, ex, ey) && p!=0)
			dp[id][p][0] = min(dp[id][p][0], Sech(id-1, p-1, 1)+3);
		dp[id][p][0] = min(dp[id][p][0], Sech(id-1, p, 0)+Jud(x, y, ex ,ey));
	}
	return dp[id][p][now];
}
int main(void)
{
	int i, j, x, ans, id;
	scanf("%d", &n);
	for(i=1;i<=n;i++)
	{
		for(j=1;j<=n;j++)
		{
			scanf("%d", &x);
			s[x].x = i, s[x].y = j;
		}
	}
	ans = 22222222;
	memset(dp, -1, sizeof(dp));
	for(i=0;i<=15;i++)
	{
		if(Sech(n*n, i, 0)<ans)
		{
			ans = Sech(n*n, i, 0);
			id = i;
		}
		if(Sech(n*n, i, 1)<ans)
		{
			ans = Sech(n*n, i, 1);
			id = i;
		}
		if(Sech(n*n, i, 2)<ans)
		{
			ans = Sech(n*n, i, 2);
			id = i;
		}
	}
	printf("%d %d\n", ans, id);
	return 0;
}
/*
8
57 45 12 22 16 43 6 29
23 46 48 37 55 1 42 49
59 21 2 8 52 30 51 19
14 18 44 50 34 58 35 53
9 41 13 25 10 62 40 7
47 56 26 15 33 63 31 39
61 38 24 3 27 20 36 28
5 64 11 32 60 54 17 4
*/

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值