【搜索-BFS】USA3.3——亚瑟王的宫殿 Camelot

前言

这道题...坑点好多...不对,应该是我自己搞出来的坑点QAQ...

心好累...搞了一上午...

题目

题目描述

很久以前,亚瑟王和他的骑士习惯每年元旦去庆祝他们的友谊。为了纪念上述事件, 我们把这些故事看作是一个棋盘游戏。有一个国王和若干个骑士被放置在一个由许多方格 组成的棋盘上,没有两个骑士在同一个方格内。

这个例子是标准的 8*8 棋盘

国王可以移动到任何一个相邻的方格,从下图中黑子位置到下图中白子位置前提是他 不掉出棋盘之外。

一个骑士可以从下图中黑子位置移动到下图中白子位置(走“日”字形) 但前提是他 不掉出棋盘之外。

在游戏中,玩家可在每个方格上放不止一个棋子,假定方格足够大,任何棋子都不会 阻碍到其他棋子正常行动。

玩家的任务就是把所有的棋子移动到同一个方格里——用最小的步数。为了完成这个 任务,他必须按照上面所说的规则去移动棋子。另外,玩家可以选择一个骑士跟国王从他 们两个相遇的那个点开始一起行动,这时他们按照骑士的行动规则行动,其他的单独骑士 则自己一直走到集中点。骑士和国王一起走的时候,只算一个人走的步数。

请计算他们集中在一起的最小步数,而且玩家必须自己找出这个集中点。当然,这些 棋子可以在棋盘的任何地方集合。

输入格式

第一行: 两个用空格隔开的整数:R,C 分别为棋盘行和列的长。不超过 26 列,40 行。

第二行到结尾: 输入文件包含了一些有空格隔开的字母/数字对,一行有一个或以 上。第一对为国王的位置,接下来是骑士的位置。可能没有骑士,也可能整个棋盘都是骑 士。行从 1 开始,列从大写字母 A 开始。

输出格式

单独一行表示棋子集中在一个方格的最小步数。

输入输出样例

输入

8 8
D 4 
A 3 A 8 
H 1 H 8 

输出

10

说明/提示

【样例说明】

他们集中在 B5。

骑士 1: A3 - B5 (1 步)

骑士 2: A8 - C7 - B5 (2 步)

骑士 3: H1 - G3 - F5 - D4 (此时国王开始与这个骑士一起走) - B5 (4 步) 骑士 4: H8 - F7 - D6 - B5 (3 步)

1 + 2 + 4 + 3 = 10 步

题目翻译来自NOCOW。

USACO Training Section 3.3

题目大意

方格棋盘上,有一些格子放了骑士或国王,一个格子只有一个,国王只有一个

每个格子可放多个棋子

骑士走“日”,王走“米”,且当一骑士在某一格与国王相遇后,二人可一起走,步数只算其中一人的

求所有骑士和国王都走到相同的某一个格子的最小总步数

分析

观察数据范围,发现十分“和蔼可亲”,于是做法可以考虑得暴力一点:

1.预处理出每个格子到另一个格子的距离,在每一个格子都做一遍BFS即可

2.枚举:

①和国王一起走的是哪个骑士

②所有棋子在哪个格子集合(目的地)

③接国王的骑士和国王在哪里相遇

3.更新答案


【“血泪史”中总结出的要点】

1.memset 初始化dis[  ]的话,很玄学,必须这样打:

memset(dis,0x10f,sizeof(dis));

0x3f 我又WA又TLE...

也可以这样初始化:

for(int i=1;i<=n;i++)
	for(int j=1;j<=m;j++)
		for(int k=1;k<=n;k++)
			for(int l=1;l<=m;l++)
				dis[i][j][k][l]=1000000;

2.注意枚举顺序和剪枝,不然TLE

3.国王不一定要和骑士一起走,注意一起走和不一起走这两种情况

4.“8*8”就是迷惑人的啊,棋盘可能比这个大,我最开始在这里卡了好久不知道哪里有问题,一定要好好看数据范围...

5.输入有技巧,gets()和单个字符输入我都试过,不好处理,所以直接质朴点,和平常一样地用“%s”

6.国王( x , y )到某个格子( i , j )的步数为max( abs( x - i ) , ( y - j ) )

因为国王走的“米”,有8个方向,不同于计算普通曼哈顿距离

奇怪的TLE代码

/*
ID:lunasmi2
TASK:camelot
LANG:C++                 
*/
#include<cstdio>
#include<cmath>
#include<queue>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN=40,INF=0x3f3f3f3f;
int dir1[10]={2,2,1,-1,-2,-2,-1,1},dir2[10]={-1,1,2,2,1,-1,-2,-2};
bool vis[MAXN+5][MAXN+5];
int dis[MAXN+5][MAXN+5][MAXN+5][MAXN+5];
int n,m,cnt,ans=INF; 
int X,Y;//国王位置(X,Y) 
struct node
{
	int x,y,step;
}a[MAXN*MAXN+5];
void BFS(int x,int y)
{
	memset(vis,0,sizeof(vis));
	vis[x][y]=1;
	dis[x][y][x][y]=0;
	queue<node> que;
	node q;
	q.x=x,q.y=y,q.step=0;
	que.push(q);
	while(!que.empty())
	{
		q=que.front();que.pop();
		for(int i=0;i<8;i++)
		{
			int dx=q.x+dir1[i],dy=q.y+dir2[i];
			if(1<=dx&&dx<=n&&1<=dy&&dy<=m&&!vis[dx][dy])
			{
				vis[dx][dy]=1;
				dis[x][y][dx][dy]=q.step+1;
				node tmp;
				tmp.x=dx,tmp.y=dy,tmp.step=q.step+1;
				que.push(tmp);
			}
		}
	}
}
int main()
{
	//freopen("camelot.in","r",stdin);
    //freopen("camelot.out","w",stdout);
    scanf("%d%d",&n,&m);
    char tmp1[5];
    int tmp2;
    scanf("%s %d",tmp1,&tmp2);
    X=tmp2;
    Y=tmp1[0]-'A'+1;
    while(~scanf("%s %d",tmp1,&tmp2))
    {
		a[++cnt].x=tmp2;
		a[cnt].y=tmp1[0]-'A'+1;
	}
	//预处理(i,j)到各方格的步数 
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			for(int k=1;k<=n;k++)
				for(int l=1;l<=m;l++)
					dis[i][j][k][l]=1000000;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
				BFS(i,j);	
	for(int k=1;k<=cnt;k++)//第x个骑士护送国王 
    	for(int i=1;i<=n;i++)
    	for(int j=1;j<=m;j++)//国王和骑士从(i,j)开始一起走 
     		for(int x=1;x<=n;x++)
    		for(int y=1;y<=m;y++)//目的地为(x,y) 
    		{
    			int d,tot=0;
    			for(int t=1;t<=cnt;t++)//统计其他骑士的步数 
    				if(t!=k)
    					tot+=dis[a[t].x][a[t].y][x][y];
	    		d=dis[a[k].x][a[k].y][i][j]+max(abs(X-i),abs(Y-j))+dis[i][j][x][y]+tot;
	    		ans=min(ans,d);
			}   
	if(ans==INF)
		printf("0\n");
	else
		printf("%d\n",ans);
	return 0;
}

微调后AC代码

#include<cstdio>
#include<cmath>
#include<queue>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN=40,INF=0x3f3f3f3f;
int dir1[10]={2,2,1,-1,-2,-2,-1,1},dir2[10]={-1,1,2,2,1,-1,-2,-2};
bool vis[MAXN+5][MAXN+5];
int dis[MAXN+5][MAXN+5][MAXN+5][MAXN+5];
int n,m,cnt,ans=INF; 
int X,Y;//国王位置(X,Y) 
struct node
{
	int x,y,step;
}a[MAXN*MAXN+5];
void BFS(int x,int y)
{
	memset(vis,0,sizeof(vis));
	vis[x][y]=1;
	dis[x][y][x][y]=0;
	queue<node> que;
	node q;
	q.x=x,q.y=y,q.step=0;
	que.push(q);
	while(!que.empty())
	{
		q=que.front();que.pop();
		for(int i=0;i<8;i++)
		{
			int dx=q.x+dir1[i],dy=q.y+dir2[i];
			if(1<=dx&&dx<=n&&1<=dy&&dy<=m&&!vis[dx][dy])
			{
				vis[dx][dy]=1;
				dis[x][y][dx][dy]=q.step+1;
				node tmp;
				tmp.x=dx,tmp.y=dy,tmp.step=q.step+1;
				que.push(tmp);
			}
		}
	}
}
int main()
{ 
    scanf("%d%d",&n,&m);
    char tmp1[5];
    int tmp2;
    scanf("%s %d",tmp1,&tmp2);
    X=tmp2;
    Y=tmp1[0]-'A'+1;
    while(~scanf("%s %d",tmp1,&tmp2))
    {
		a[++cnt].x=tmp2;
		a[cnt].y=tmp1[0]-'A'+1;
	}
	//预处理(i,j)到各方格的步数 
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			for(int k=1;k<=n;k++)
				for(int l=1;l<=m;l++)
					dis[i][j][k][l]=1000000;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			BFS(i,j);	
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)//集合位置(i,j) 
    {
    	int tot=0;
    	for(int k=1;k<=cnt;k++)//统计其他骑士的步数 
    		tot+=dis[a[k].x][a[k].y][i][j];
		ans=min(ans,tot+max(abs(X-i),abs(Y-j)));
		for(int k=1;k<=cnt;k++)
		{
			int dx=a[k].x,dy=a[k].y;
			int t=tot-dis[dx][dy][i][j];
			if(t>=ans)//最优性剪枝
				continue;
			for(int x=1;x<=n;x++)
			for(int y=1;y<=m;y++)
				ans=min(ans,t+dis[x][y][dx][dy]+dis[x][y][i][j]+max(abs(X-x),abs(Y-y)));
		} 		
	} 
	printf("%d\n",ans);
	return 0;
}

总结

虽然自己打出的代码与正解大致相同,但是最初仍没有AC,还需历练啊

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值