¤P1930 亚瑟王的宫殿 Camelot(美妙pick up)

P1930亚瑟王的宫殿 Camelot传送门

题目描述

很久以前,亚瑟王和他的骑士习惯每年元旦去庆祝他们的友谊。为了纪念上述事件,我们把这些故事看作是一个棋盘游

。有一个国王和若干个骑士被放置在一个由许多方格成的棋盘上,没有两个骑士在同一个方格内。

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


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

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

 (国王的步伐)    (骑士的步伐)

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

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

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

输入输出格式

输入格式:

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

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

输出格式:

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

输入输出样例

输入样例#1

8  8

D  4

A  3  A  8

H  1 H  8

输出样例#1

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

------------------------------------------------------------------------------------------------------------------------------------------

Camelot 题解:



先自行给棋盘编号,如(A,1)为1号,(C,4)为15号。方便数组使用。

(如:15号点的编号=(4-1)*4+3)

 

步骤:(三步走

  1求出各点间之间以骑士的步伐走所需要的最小步数  O((R*C)2)

(d[i][j]: i到j所需要的最小步数)

解法:

循环+Bfs:

 for i :=1to R*C (以每一个点作为起点)

   bfs();

 

  2求出在无国王影响下,所有骑士到达棋盘上某点(集合点)的总步数  O((R*C)2)

     (asse[i]: 以i号点作为集合点的步数和)

(不管国王)

解法:

   双重循环枚举集合点和骑士)

 

3求出题目要求的最小步数O((R*C)2)  

   (两种情况:<1>:国王的”九宫的约会”,即王被骑士接应再后去集合点;

                Ps:(国王被接应的地点只会在”九宫格内”,可自己加以证明)

<2>国王直接步行去集合点;

求最小值;)

解法:

 For i :=1 toR*C   (枚举集合点)

{

    1) 若不接王:(骑士,国王都直接去集合点)

计算总步数:p1=asse[i]+cal(i);(cal(i):自定义的函数,求王走到i点的步数)

    2)若接王:(则王被接应的地点在”九宫格”内)(让一个骑士去接王,其他的骑士直接去集合点)

for j :=1 to 9  (枚举”九宫格”)

 for k :=1 toall(knight) (枚举所有骑士,让每个骑士尝试去接王,找出最优解)

计算总步数: p2=asse[i]-d[i ][k](骑士k不直接到i点)+d[k][王的位置]+d[王的位置][i];

   3)取最小值:

Ans=min(ans,min(p1,p2));

}                      

      

 

  *其他需注意的细节:

   1、因为要将每个点作为起点进行Bfs(),所以vis[]数组要记得清空

   2、d[][]数组要赋初值为INF,因为可能在某些棋盘上的某两点按骑士的步伐走不到,如图,1号点无法到2号点。


     3、因为的d[i][j]的值可能为INF,所以计算asse[]数组的值时要注意,若组成某个asse[i]的d[i][j]的值为INF,则直接把asse[i]赋值为INF,表示这个点无法作为集合点(因为有骑士到不了该集合点)

   4、计算总步数p2时,若某骑士到i的步数为INF(表示到不了),要特判一下,否则减了一个值为INF的d[i][k]后,p2为很小值(负数),然后错误地被Ans取走。

------------------------------------------------------------------------------------------------------------------------------------------

AC代码:

#include
    
    
     
     
#include
     
     
      
      
#include
      
      
       
       
#include
       
       
        
        
#define go(i,a,b) for(int i=a;i<=b;i++)
#define inf 0x3f3f3f
using namespace std;
int dx[9]={0,-1,-2,-2,-1,1,2,2,1};
int dy[9]={0,2,1,-1,-2,-2,-1,1,2};
int kkx[10]={0,-1,-1,0,1,1,1,0,-1,0};
int kky[10]={0,0,1,1,1,0,-1,-1,-1,0};
struct haha{int v,sp,pre;};
int C,R,x[1042],y[1042],kx,ky,asse[1042];
int d[1042][1042];
haha q[1042];
bool vis[45][30];
int A(int w){return w>0?w:-w;};
int cal(int xx,int yy)                //计算王到任意点的距离 
{
	int derx=A(xx-kx);
	int dery=A(yy-ky);
	return A(derx-dery)+min(derx,dery);
}
int main()
{
	freopen("camelot.in","r",stdin);
	freopen("camelot.out","w",stdout);
	scanf("%d%d\n",&C,&R);
	memset(d,inf,sizeof(d));
	go(i,1,R*C)d[i][i]=0;
	int a,cnt=0;
	char b;
	scanf("%c %d\n",&b,&a);
	kx=a;
	ky=b-'A'+1;
	while(scanf("%c %d\n",&b,&a)!=EOF)    // 读入处理      
	{
		cnt++;
		x[cnt]=a;
		y[cnt]=b-'A'+1;
	}
	go(i,1,C)     // 循环+bfs  步骤一   
	go(j,1,R)
	{
		memset(vis,0,sizeof(vis));
	    int l=1,r=1;	
	    q[1].v=R*(i-1)+j;
		q[1].sp=0;
		vis[i][j]=true;
		while(l<=r)
		{
			haha tmp=q[l++];
			int sy=tmp.v%R;
			if(sy==0) sy=R;
			int sx=(tmp.v-sy)/R+1;
			int stp=tmp.sp;
			go(k,1,8)
			{
				int nx=sx+dx[k];
				int ny=sy+dy[k];
				if(nx>=1&&nx<=C&&ny>=1&&ny<=R&&!vis[nx][ny])
				{
					r++;
					q[r].v=R*(nx-1)+ny;
					q[r].sp=stp+1;
					q[r].pre=tmp.v;
					vis[nx][ny]=1;
					
					int cod=(nx-1)*R+ny;
					d[q[1].v][cod]=q[r].sp;
				}
				
			}

		}
	}

	int ans=inf;      //双重循环 步骤二 
	go(i,1,C)
	go(j,1,R)
	{
	  int cod=R*(i-1)+j;
	  go(j,1,cnt)
	  {
	  	 int code=R*(x[j]-1)+y[j];
	  	 if(d[cod][code]==1061109567) 
			 {
			 	asse[cod]=inf;
			 	break;
			 }
	     asse[cod]+=d[cod][code];
	   
      }
    }
    go(i,1,C)       //步骤三 求最后答案 
    go(j,1,R)
    {
	   int acode=R*(i-1)+j;
       go(k,1,9)
       {
       	  int mx=kx+kkx[k];
       	  int my=ky+kky[k];
       	  if(mx<=0||mx>C||my<=0||my>R)continue;
       	  go(u,1,cnt)
       	  {
       	  	 int mcode=(mx-1)*R+my;
       	  	 int kcode=(x[u]-1)*R+y[u];
       	  	 int meet=d[mcode][kcode];
       	  	 if(k<9)meet++;
       	  	 int origin=asse[acode];
       	  	 if (origin==inf||d[acode][kcode]==1061109567) continue; 
       	  	 int present=origin-d[acode][kcode]+meet+d[acode][mcode];
       	  	 int presentt=origin+cal(i,j);
       	  	 ans=min(ans,min(present,presentt));
       	  }
       }
    }
    if(x[1]==0)printf("0");   //特判,无骑士。 
	else printf("%d",ans);
	return 0;
}
       
       
      
      
     
     
    
    

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值