usaco Chapter 3 section 3.3 Camelot

本文介绍了一款棋盘游戏的算法实现,玩家需将棋盘上的国王和骑士聚集到同一位置,通过计算不同棋子间的最短路径并考虑国王携带骑士的特殊移动方式,最终得出最少移动步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

/*

Camelot
IOI 98

Centuries ago, King Arthur and the Knights of the Round Table used to meet every year on New Year's Day to celebrate their fellowship. In remembrance of these events, we consider a board game for one player, on which one chesspiece king and several knight pieces are placed on squares, no two knights on the same square.

This example board is the standard 8x8 array of squares:

The King can move to any adjacent square from to as long as it does not fall off the board:

A Knight can jump from to , as long as it does not fall off the board:

During the play, the player can place more than one piece in the same square. The board squares are assumed big enough so that a piece is never an obstacle for any other piece to move freely.

The player's goal is to move the pieces so as to gather them all in the same square - in the minimal number of moves. To achieve this, he must move the pieces as prescribed above. Additionally, whenever the king and one or more knights are placed in the same square, the player may choose to move the king and one of the knights together from that point on, as a single knight, up to the final gathering point. Moving the knight together with the king counts as a single move.

Write a program to compute the minimum number of moves the player must perform to produce the gathering. The pieces can gather on any square, of course.

*/

/*
ID: niepeng1
LANG: C++
TASK: camelot

不得不承认,我又参考了别人的程序。这次题目完全没有思路。最后看了题解发现国王搭载骑士的地点有限制只能在-2,+2 row col之间

最后发现时一次计算所有节点间的骑士步数,然后枚举结束点。计算没有国王的距离,最后再加上搭载国王的最短增加的步数就可以得到结果了。
*/
#include <iostream>
using namespace std;
#define MaxR 35
#define MaxC 30
FILE *in,*out;
int minSteps[MaxR][MaxC][MaxR][MaxC];
int visited[MaxR][MaxC];
int path[8][2]={
 -1,-2,
 -2,-1,
 -2, 1,
 -1, 2,
  1, 2,
  2, 1,
  2,-1,
  1,-2
};
struct lovekid{
 int row;//行
 int column;//列
}King,Knight[MaxR*MaxC],queue[MaxR*MaxC];
int R,C,cnt,ans=1<<30;

void Init()
{
 for(int i=1;i<=R;i++)
  for(int j=1;j<=C;j++)
   for(int ii=1;ii<=R;ii++)
    for(int jj=1;jj<=C;jj++)
     minSteps[i][j][ii][jj]=1<<25;
}
void Refresh()
{
 for(int i=1;i<=R;i++)
  for(int j=1;j<=C;j++)
   visited[i][j]=false;
}
int KingWay(int r,int c)
{
 int row=King.row-r;
 if(row<0)
  row=-row;
 int col=King.column-c;
 if(col<0)
  col=-col;
 return row>col?row:col;
}
void Bfs()
{
  int r,c,k,left,right,preR,preC,currentR,currentC;
  Init();
  for(r=1;r<=R;r++)
   for(c=1;c<=C;c++){
   Refresh();//更新访问标记
   left=right=0;//初始化数据
   queue[right].column=c;
   queue[right++].row=r;
   visited[r][c]=true;
   minSteps[r][c][r][c]=0;
   while(left<right){
    preR=queue[left].row;preC=queue[left++].column;
    for(k=0;k<8;k++){
  currentR=preR+path[k][0];currentC=preC+path[k][1];
  if(currentR>0&&currentR<=R&&currentC>0&&currentC<=C&&
   !visited[currentR][currentC]){
    queue[right].column=currentC;
    queue[right++].row=currentR;
    minSteps[r][c][currentR][currentC]=minSteps[r][c][preR][preC]+1;
    visited[currentR][currentC]=true;
  }
    }
   }
   }
}

void Solve(){
 int i,j,k,total,diffrence,r,c,kr,kc,tmp;
 bool cut;//剪枝,剪掉骑士不能到达的格子
 Bfs();
 for(i=1;i<=R;i++)
  for(j=1;j<=C;j++){//枚举终点
   total=0;
   cut=false;
    for(k=0;k<cnt;k++)
  if(minSteps[Knight[k].row][Knight[k].column][i][j]>=(1<<25)){
   cut=true;//遍历所有的骑士节点,如果有一个不能到,就放弃该节点
            break;
  }
  else total+=minSteps[Knight[k].row][Knight[k].column][i][j];
  if(cut||total>ans)continue;/*剪掉不能到达的格子和还没算王的路径已经大于或目前最有解的值格子*/
    diffrence=KingWay(i,j);
 //total存储当前不包含王的所有骑士汇聚到目标节点的距离
    for(r=-2;r<=2;r++)//最优解在的范围是王的坐标+-2
     for(c=-2;c<=2;c++){
      kr=King.row+r;
      kc=King.column+c;
      if(kr>0&&kr<=R&&kc>0&&kc<=C)//在边界内
       for(k=0;k<cnt;k++){//遍历所有骑士,如果求最小的载王去目的地点的距离
         tmp=minSteps[Knight[k].row][Knight[k].column][kr][kc]+
          minSteps[kr][kc][i][j]+KingWay(kr,kc)-
          minSteps[Knight[k].row][Knight[k].column][i][j];
         if(diffrence>tmp)//difference存储王直接走到目标节点的步数
          diffrence=tmp;
        }
     }
     total+=diffrence;
     if(ans>total)
      ans=total;
  }
  fprintf(out,"%d/n",ans);
}
void Read(){//读入数据
 in=fopen("camelot.in","r");
 out=fopen("camelot.out","w");
 char str[2];
 int input;
 fscanf(in,"%d%d",&R,&C);
 fscanf(in,"%s%d",str,&input);
 King.row=input;King.column=str[0]-'A'+1;//王的坐标
 while(fscanf(in,"%s%d",str,&input)!=EOF){//骑士的坐标
  Knight[cnt].row=input;Knight[cnt++].column=str[0]-'A'+1;
 }

}
int main()
{
 Read();
 Solve();
 return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值