Description
于大夫建造了一个美丽的池塘,用来让自己愉快的玩耍。这个长方形的池子被分割成了M 行和N 列的正方形格子。池塘中有些地方是可以跳上的荷叶,有些地方是不能放置荷叶也不能跳上的岩石,其他地方是池水(当然于大夫也是不能游泳的)。于大夫十分有趣,他在池塘跳跃的方式和象棋中的马一样可以向八个方向走日字形,而且于大夫只能跳上荷叶。于大夫每天从一个给定的有荷叶的地方出发,试图到达另一个给定的有荷叶的地方。但有一天他发现自己无论如何也不能到达目的地了,除非再在水中放置几个荷叶。于大夫想让你告诉他,最少还需放置几片荷叶?在放置荷叶最少的前提下,最少需要几步能到达目的地?
Data Constraint
10%的数据n,m<=4
30%的数据n,m<=10
50%的数据n,m<=30
70%的数据n,m<=50
100%的数据n,m<=100
Solution
其实这道题目就是要你在跳跃荷叶最少的情况下求最短路。不难发现,其实荷叶最少也是在求最短路。所以题目变成求一条以荷叶为第一关键字,走的步数为第二关键字的最短路。用spfa跑一下即可(和正常的只有一个关键字的spfa类似)。
具体做法为:我们设F[i][j]为到达(i,j)所需的最少荷叶数量,G[i][j]表示在到达(i,j)所需的最少荷叶数量的情况下,所需的最少步数。那么对于一个点X,他假设能更新另一个点Y的F值,那么就将更新的点Y加入队列,另外,这个点X的F值和点Y的F值相同,但点X的G值能更新点Y的G值,那么同样将点Y加入队列
Code
#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn=105;
const int c[8][2]={{2,1},{1,2},{2,-1},{-1,2},{-2,1},{1,-2},{-1,-2},{-2,-1}};
int f[maxn][maxn],g[maxn][maxn],v[maxn*maxn*maxn][2],a[maxn][maxn];
int n,i,t,j,k,l,x,m,y,xx,yy,ex,ey,p;
bool bz[maxn][maxn];
int main(){
freopen("lilypad.in","r",stdin);freopen("lilypad.out","w",stdout);
scanf("%d%d",&n,&m);memset(f,127,sizeof(f));
p=f[1][1];
for (i=1;i<=n;i++)
for (j=1;j<=m;j++){
scanf("%d",&a[i][j]);
if (a[i][j]==3)v[1][0]=i,v[1][1]=j,bz[i][j]=true,f[i][j]=0;
else if (a[i][j]==4) ex=i,ey=j;
if (a[i][j]>2) a[i][j]=1;
}
j=1;i=0;
while (i<j){
xx=v[++i][0];yy=v[i][1];
for (k=0;k<8;k++){
x=xx+c[k][0];y=yy+c[k][1];
if (x<1 || y<1 || x>n || y>m || a[x][y]>1) continue;
if (!a[x][y]) t=f[xx][yy]+1;
else t=f[xx][yy];
if (f[x][y]>t || f[x][y]==t && g[xx][yy]+1<g[x][y]){
f[x][y]=t,g[x][y]=g[xx][yy]+1;
if (bz[x][y]) continue;
bz[x][y]=true;v[++j][0]=x;v[j][1]=y;
}
}
bz[xx][yy]=false;
}
if (f[ex][ey]!=p) printf("%d %d\n",f[ex][ey],g[ex][ey]);
else printf("-1 -1\n");
}