地址:http://codeforces.com/contest/1065/problem/D
思路:最短路+dp,将每一点的x,y,走的棋子等信息压缩成一个状态 x*n*3+y*3+p;
在初始化全部点的连接状态,用floyd求出最短路,在用dp来求解
Code:
#include<iostream>
using namespace std;
typedef pair<int,int> pt; //<最小步数,最小换棋子数>
const int MAX_N=15;
const int MAX_M=305;
const int INF=1e9;
const int dx[]={-2, -1, 1, 2, 2, 1, -1, -2};
const int dy[]={ 1, 2, 2, 1, -1, -2, -2, -1};
int n;
int a[MAX_N][MAX_N];
pt d[MAX_N*MAX_N];
pt G[MAX_M][MAX_M];
pt dp[MAX_N*MAX_N][5];
int get(int x,int y,int p){
return x*n*3+y*3+p;
}
bool judge(int x,int y){
return (x>=0&&x<n&&y>=0&&y<n);
}
pt operator+ (const pt &a,const pt &b){
return pt(a.first+b.first,a.second+b.second);
}
int main()
{
cin>>n;
for(int i=0;i<n;++i)
for(int j=0,x;j<n;++j)
{
cin>>a[i][j]; --a[i][j];
d[a[i][j]]=pt(i,j);
}
for(int i=0;i<MAX_M;++i)
for(int j=0;j<MAX_M;++j)
G[i][j]=pt(INF,INF);
for(int i=0;i<MAX_M;++i)
G[i][i]=pt(0,0);
for(int x=0;x<n;++x)
for(int y=0;y<n;++y)
{
pt pt0=pt(1,0);
int xi,yi,u=get(x,y,0);
for(int i=0;i<8;++i) //0:马走一步
{
xi=x+dx[i]; yi=y+dy[i];
if(judge(xi,yi)) G[u][get(xi,yi,0)]=pt0;
}
u=get(x,y,1);
for(int i=-n;i<=n;++i) //1:帅走一步
{
xi=x+i; yi=y+i;
if(judge(xi,yi)) G[u][get(xi,yi,1)]=pt0;
yi=y-i;
if(judge(xi,yi)) G[u][get(xi,yi,1)]=pt0;
}
u=get(x,y,2);
for(int i=0;i<n;++i) //2:车走一步
{
xi=x; yi=i;
if(!(xi==x&&yi==y)) G[u][get(xi,yi,2)]=pt0;
xi=i; yi=y;
if(!(xi==x&&yi==y)) G[u][get(xi,yi,2)]=pt0;
}
for(int i=0;i<3;++i) //换棋子
for(int j=0;j<3;++j)
if(i!=j) G[get(x,y,i)][get(x,y,j)]=pt(1,1);
}
for(int k=0;k<MAX_M;++k) //floyed求最短路
for(int i=0;i<MAX_M;++i)
for(int j=0;j<MAX_M;++j)
G[i][j]=min(G[i][j],G[i][k]+G[k][j]);
for(int i=0;i<=n*n;++i)
dp[i][0]=dp[i][1]=dp[i][2]=pt(INF,INF);
dp[0][0]=dp[0][1]=dp[0][2]=pt(0,0);
for(int i=0;i<n*n-1;++i)
for(int j=0;j<3;++j)
for(int k=0;k<3;++k)
{
int u=get(d[i].first,d[i].second,k),v=get(d[i+1].first,d[i+1].second,j);
dp[i+1][j]=min(dp[i+1][j],dp[i][k]+G[u][v]);
}
pt ans=dp[n*n-1][0];
ans=min(ans,dp[n*n-1][1]);
ans=min(ans,dp[n*n-1][2]);
cout<<ans.first<<" "<<ans.second<<endl;
return 0;
}