D. Three Pieces

在这里插入图片描述

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

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

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

思路:
首先可以用设置状态dist[X][Y],表示从状态X到状态Y所移动的最小步数和对应的最少换棋子数,这里的X和Y是表示的状态是所在位置与当前使用的棋子

然后使用floyed算法计算dist[X][Y]之间的最短路径

dp[i][k]表示走到棋盘第i个格子,当前棋子是第k种棋子时得到的最少步数和对应的最少换棋子次数(存储的是pair)

#include <bits/stdc++.h>

#define forn(i, n) for (int i = 0; i < int(n); i++)
#define mp make_pair
#define x first
#define y second

using namespace std;

typedef pair<int, int> pt;//first表示最少步数,second表示最少换棋子

const int N = 12;
const int M = 305;
const int INF = 1e9;

int n;
int a[N][N];
pt pos[N * N];//行进点的顺序
pt dist[M][M];

pt operator +(const pt &a, const pt &b){
    return mp(a.x + b.x, a.y + b.y);
}

int dx[] = {-2, -1, 1, 2,  2,  1, -1, -2};
int dy[] = { 1,  2, 2, 1, -1, -2, -2, -1};

bool in(int x, int y){
    return (0 <= x && x < n && 0 <= y && y < n);
}
//x,y,p表示在位置(x,y)使用棋子p,这里使用一维数组表示三维状态
int get(int x, int y, int p){
    return x * n * 3 + y * 3 + p;
}

pt dp[N * N][3];

int main() {
    scanf("%d", &n);
    forn(i, n) forn(j, n){
        scanf("%d", &a[i][j]);
        --a[i][j];
        pos[a[i][j]] = mp(i, j);
    }
    
    forn(i, M) forn(j, M) dist[i][j] = mp(INF, INF);
    forn(i, M) dist[i][i] = mp(0, 0);
    //初始化从位置(x,y)使用3种棋子走一步后对应状态的距离
    forn(x, n) forn(y, n){
        forn(i, 8){                               //0:马走一步 
            int nx = x + dx[i];
            int ny = y + dy[i];
            if (in(nx, ny))
                dist[get(x, y, 0)][get(nx, ny, 0)] = mp(1, 0);
        }
        
        for (int i = -n + 1; i <= n - 1; ++i){    //1:帅走一步
            int nx = x + i;
            int ny = y + i;
            if (in(nx, ny))
                dist[get(x, y, 1)][get(nx, ny, 1)] = mp(1, 0);
            ny = y - i;
            if (in(nx, ny))
                dist[get(x, y, 1)][get(nx, ny, 1)] = mp(1, 0);
        }
        
        forn(i, n){                               //2:车走一步
            int nx = x;
            int ny = i;
            dist[get(x, y, 2)][get(nx, ny, 2)] = mp(1, 0);
            nx = i;
            ny = y;
            dist[get(x, y, 2)][get(nx, ny, 2)] = mp(1, 0);
        }
       //换棋子
        forn(i, 3) forn(j, 3){
            if (i != j){
                dist[get(x, y, i)][get(x, y, j)] = mp(1, 1);
            }
        }
    }
    //使用floyed
    forn(k, M) forn(i, M) forn(j, M)
        dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);
    //动态规划计算最优遍历
    forn(i, N * N) forn(j, 3) dp[i][j] = mp(INF, INF);
    dp[0][0] = dp[0][1] = dp[0][2] = mp(0, 0);
    forn(i, n * n - 1) forn(j, 3) forn(k, 3)
        dp[i + 1][k] = min(dp[i + 1][k], dp[i][j] + dist[get(pos[i].x, pos[i].y, j)][get(pos[i + 1].x, pos[i + 1].y, k)]);
    
    pt ans = mp(INF, INF);
    ans = min(ans, dp[n * n - 1][0]);
    ans = min(ans, dp[n * n - 1][1]);
    ans = min(ans, dp[n * n - 1][2]);
    
    printf("%d %d\n", ans.x, ans.y);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43870114/article/details/90043083