Codeforces 1065D Three Pieces

题意:

有一个n * n 的矩阵,不重不漏地随机放着 1到 n*n 的每个数。现在你站在数字1的位置,每次可以有三种走法:

  • 走直线,四个方向。一次可以走多个单位,不仅只能走到相邻的位置,就像中国象棋里的车。
  • 走斜线,四个方向。一次可以走多个单位,像国际象棋里的象。
  • 走日字,八个方向,一次只能走一个单位。就像中国象棋里的马。
    每走一次,需要花费一个精力值。如果切换了一次走法,需要额外花费一个精力值。而且要求,必须先访问1,再访问2,然后是3,以此类推,一直到n^2,但是,在从 i 走到 i+1的过程中,可以经过其它的点,比如在中途某个点停一下,换了走法,继续走。

问最少花费多少精力值可以走完n^2个点。精力最小的时候要求切换走法次数也最少。
输出最小精力和最少切换走法次数。

分析:

bfs搜索,状态可以这么设定dp[i][j][z][t][k]表示到达g[i][j]这个点,用的是第z种走法,已经换了t次走法,已经按顺序访问了k个点,此时的最小精力值。
转移分为两种:

  • 不换方式,按照当前方式继续走下去。
  • 原地换一下方式,换完不动。

代码:

#include <bits/stdc++.h>
#define ms(a,b) memset(a,b,sizeof(a))
#define ll long long
#define PII pair<int,int>
#define sz(x) (int)(x.size())
using namespace std;
const int INF = 0x3f3f3f3f;
struct node{
    int x,y,z,t,k;
    node(int _x,int _y,int _z,int _t,int _k) {
        x = _x;
        y = _y;
        z = _z; //当前方式
        t = _t; //换了多少次
        k = _k; //经过前k个点
    }
};
int n;
int g[15][15];
int dp[11][11][3][201][101]; //0是日,1是直,2是斜线
int dx1[8][2] = {{-2,-1},{-2,1},{2,-1},{2,1},{-1,-2},{-1,2},{1,-2},{1,2}}; //日
int dx2[4][2] = {{-1,0},{1,0},{0,1},{0,-1}}; //直线
int dx3[4][2] = {{-1,-1},{-1,1},{1,-1},{1,1}}; //斜线
int sx,sy,tx,ty;
void bfs(int sx,int sy){
    memset(dp,-1,sizeof dp);
    dp[sx][sy][0][0][1] = dp[sx][sy][1][0][1] = dp[sx][sy][2][0][1] = 0;
    queue<node> q;
    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 nd = q.front();q.pop();
        int x = nd.x, y = nd.y, z = nd.z, t = nd.t , k = nd.k;
        //原地换走法
        for(int i=0;i<3;i++){
        	if(i==z)	continue;
        	if(dp[x][y][i][t+1][k]!=-1) continue;
        	dp[x][y][i][t+1][k] = dp[x][y][z][t][k]+1;
        	q.push(node(x,y,i,t+1,k));

        }
        //不换状态继续走
        if(z==0) {
            for(int i=0;i<8;i++){
                int xx = x+dx1[i][0], yy = y + dx1[i][1],kk = k;
                if(xx<1 || xx>n || yy<1 || yy>n) continue;
                if(g[xx][yy] == k+1) kk++;
                if(dp[xx][yy][z][t][kk]!=-1) continue;
                dp[xx][yy][z][t][kk] = dp[x][y][z][t][k]+1;
                q.push(node(xx,yy,z,t,kk));
            }
        }
        if(z==1) {
            for(int j=1;j<=10;j++){
                for(int i=0;i<4;i++){
	                int xx = x+j*dx2[i][0], yy = y + j*dx2[i][1],kk = k;
	                if(xx<1 || xx>n || yy<1 || yy>n) continue;
	                if(g[xx][yy] == k+1) kk++;
	                if(dp[xx][yy][z][t][kk]!=-1) continue;
	                dp[xx][yy][z][t][kk] = dp[x][y][z][t][k]+1;
	                q.push(node(xx,yy,z,t,kk));
            	}
            }
            
        }
        if(z==2) {
            for(int j=1;j<=10;j++){
                for(int i=0;i<4;i++){
	                int xx = x+j*dx3[i][0], yy = y + j*dx3[i][1],kk = k;
	                if(xx<1 || xx>n || yy<1 || yy>n) continue;
	                if(g[xx][yy] == k+1) kk++;
	                if(dp[xx][yy][z][t][kk]!=-1) continue;
	                dp[xx][yy][z][t][kk] = dp[x][y][z][t][k]+1;
	                q.push(node(xx,yy,z,t,kk));
            	}
            }
        }
    }
}
int main(){
    cin>>n;
    
    for(int i=1;i<=n;i++){
	    for(int j=1;j<=n;j++) {
	        cin>>g[i][j];
	        if(g[i][j]==1) {
	            sx = i;
	            sy = j;
	        }
	        if(g[i][j] == n*n){
	        	tx = i;
	        	ty = j;
	        }
	    }
    }
    bfs(sx,sy);
    int ans = INF;
    for(int z=0;z<3;z++){ //当前方式
    	for(int t=0;t<201;t++){ //换了多少次
    		if(dp[tx][ty][z][t][n*n]!=-1){
    			ans = min(dp[tx][ty][z][t][n*n],ans);
    		}
    	}
    }
    int ok=0;
    for(int t=0;t<201;t++){
    	for(int z=0;z<3;z++){
    		if(dp[tx][ty][z][t][n*n] == ans){
    			cout << ans << " " << t << endl;
    			ok = 1;	break;
    		}
    	}
    	if(ok)	break;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值