【搜索-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,還需歷練啊

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章