Educational Codeforces Round 52 (Rated for Div. 2) D. Three Pieces 最短路dp

题目链接:http://codeforces.com/contest/1065/problem/D

 

题意:

      给你三种棋子,和一个n*n大小的棋盘,棋盘上是1~n*n的不同数字,你可以任意选择一种棋子从数字1的位置开始,每种棋子都有自己的走法,骑士是马字条,法师可以在45°的斜角上走到任意位置,石头是垂直方向上走到任意位置,每走一次算一次操作,你在走到一个位置时都可以换其他的棋子(当然这也算增加一次操作),问你走到n*n这个位置上最少的操作数和这个操作数下最少

做法:

     下面代码中有部分备注了,dp[x][y][k][t][z]是走到第x,y这个点 角色为k 换了t次角色 走过了z个点的最小步数,每次都要判断换棋子的情况即可,就是要注意细节。。表示bug de了我好久。。心塞,这里还有个问题,就是我用了优先队列答案反而是错的,用队列就可以过了,我想可能队列更像是模拟了真实情况下的操作,没有打乱内部顺序吧,毕竟每个情况只会被走到一次,可能优先队列在某种情况下只是在前面占优势,最后还是其他的情况好。


#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
 
using namespace std;
const int maxn=105;
const int inf = 0x3f3f3f;
typedef pair<int,int> pii;
 
//0为骑士,1为牧师,2为石头
int dp[15][15][3][50][105],n,mp[15][15];
//dp[x][y][k][t][z]
//走到第x,y这个点 角色为k 换了t次角色 走过了z个点的最小步数
int dir0[8][2]={{1,2},{1,-2},{-1,2},{-1,-2},{2,1},{2,-1},{-2,1},{-2,-1}};
int dir1[4][2]={{1,1},{1,-1},{-1,1},{-1,-1}};
int dir2[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
 
struct node{
    int x,y,k,t,z;
    node(){}
    node(int x,int y,int k,int t,int z):x(x),y(y),k(k),t(t),z(z){}
    /*bool operator < (const node &a)const{
        if(z==a.z){
            if(t==a.t) return dp[x][y][k][t][z]>dp[a.x][a.y][a.k][a.t][a.z];
            else return t>a.t;
        }
        return z>a.z;
    }*/
};
queue<node> q;
int ck(int x,int y){
    if(x>0&&x<=n&&y>0&&y<=n) return 1;
    return 0;
}
void deal0(node tmp){
    for(int i=0;i<3;i++){
        if(i!=tmp.k){
            if(dp[tmp.x][tmp.y][i][tmp.t+1][tmp.z]==-1){
                dp[tmp.x][tmp.y][i][tmp.t+1][tmp.z]=dp[tmp.x][tmp.y][tmp.k][tmp.t][tmp.z]+1;
                //printf("dp[%d][%d][%d][%d][%d] = %d\n",tmp.x,tmp.y,i,tmp.t+1,tmp.z,dp[tmp.x][tmp.y][i][tmp.t+1][tmp.z]);
                q.push(node(tmp.x,tmp.y,i,tmp.t+1,tmp.z));
            }
        }
    }
}
void deal1(node tmp,int nx,int ny){
    int zz=tmp.z;
    if(mp[nx][ny]==tmp.z+1) zz++;
    if(dp[nx][ny][tmp.k][tmp.t][zz]==-1){
        dp[nx][ny][tmp.k][tmp.t][zz]=dp[tmp.x][tmp.y][tmp.k][tmp.t][tmp.z]+1;
        //printf("dp[%d][%d][%d][%d][%d] = %d\n",nx,ny,tmp.k,tmp.t,zz,dp[nx][ny][tmp.k][tmp.t][zz]);
        q.push(node(nx,ny,tmp.k,tmp.t,zz));
    }
}
void bfs(int sx,int sy){
    dp[sx][sy][0][0][1]=dp[sx][sy][1][0][1]=dp[sx][sy][2][0][1]=0;
    q.push(node(sx,sy,0,0,1));
    q.push(node(sx,sy,1,0,1));
    q.push(node(sx,sy,2,0,1));
    while(!q.empty()){
        node tmp=q.front(); q.pop();
        deal0(tmp);
        if(tmp.k==0){
            for(int i=0;i<8;i++){
                int nx=tmp.x+dir0[i][0],ny=tmp.y+dir0[i][1];
                if(ck(nx,ny))
                    deal1(tmp,nx,ny);
            }
        }
        else if(tmp.k==1){
            for(int i=0;i<4;i++){
                int nx=tmp.x+dir1[i][0],ny=tmp.y+dir1[i][1];
                while(ck(nx,ny)){
                    deal1(tmp,nx,ny);
                    nx+=dir1[i][0];
                    ny+=dir1[i][1];
                }
            }
        }
        else{
            for(int i=0;i<4;i++){
                int nx=tmp.x+dir2[i][0],ny=tmp.y+dir2[i][1];
                while(ck(nx,ny)){
                    deal1(tmp,nx,ny);
                    nx+=dir2[i][0];
                    ny+=dir2[i][1];
                }
            }
        }
    }
}
int main() {
    int sx,sy,ex,ey;
    scanf("%d",&n);
    rep(i,1,n){
        rep(j,1,n){
            scanf("%d",&mp[i][j]);
            if(mp[i][j]==1) sx=i,sy=j;
            else if(mp[i][j]==n*n) ex=i,ey=j;
        }
    }
    memset(dp,-1,sizeof(dp));
    bfs(sx,sy);
    int ans1=100000000,ans2=200;
    rep(i,0,2){
        rep(j,0,45){
            if(dp[ex][ey][i][j][n*n]!=-1){
                ans1=min(ans1,dp[ex][ey][i][j][n*n]);
            }
        }
    }
    rep(i,0,2){
        rep(j,0,45){
            if(dp[ex][ey][i][j][n*n]==ans1){
                ans2=min(ans2,j);
            }
        }
    }
    printf("%d %d\n",ans1,ans2);
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值