codeforceECround52div2DThree Pieces(图论)

版权声明:若转载请附上原博客链接,谢谢! https://blog.csdn.net/Link_Ray/article/details/83338765

题意

给定一个 n n n*n 的网格,每个网格都由 1 , 2 , 3 , 4 , , n n 1,2,3,4,\dots ,n*n 中的数字填充,一开始从1号格开始走,有三种走法。

  1. 走日字格
  2. 横走或竖走(走的格子不限)
  3. 沿着对角线走(走的格子不限)

每个格子可以重复行走。
每走到一个格子行走次数+1,同时当每次切换不同的行走方式时,行走次数也要+1,问从1号到2号再到3号…一直到 n n n*n 号格子的最少行走次数是多少,当行走次数相同,那最少的切换次数是多少。

题解

因为我们需要知道从某个点到另一个点之间的最短路径,所以需先计算一下最短路。
先定义一个 p a i r pair f i r s t first 代表行走次数, s e c o n d second 代表切换次数用来代表边权。
每个格子可以分成三个点, ( x , y , p 1 ) , ( x , y , p 2 ) , ( x , y , p 3 ) p i (x,y,p_1),(x,y,p_2),(x,y,p_3),p_i 代表行走的方式,这里可以将点的状态进行压缩一下变成 x n 3 + y 3 + p x*n*3+y*3+p ,然后将所有的点都连接起来,因为切换方式只会在自己格子中切换(不能边走边换)所以 p p 不相同的点,只需与自己的格子中的点连边,边权为 ( 1 , 1 ) (1,1) ,然后所有格子中 p p 相同的点再连边,边权为 ( 1 , 0 ) (1,0)

然后跑一遍floyd算出每两点之间的最短路径,因为每个点都有三种状态,我们不知道哪种开始走是最优的,所以需要dp递推一下。所以就从 1 1 号点开始dp直到 n n n*n 号点。

代码

```#include <bits/stdc++.h>
using namespace std;
#define x first
#define y second
const int maxn = 305;
const int M = 305;
const int inf = 0x3f3f3f3f;

const int dr[] = {-2, -1, 1, 2,  2,  1, -1, -2};
const int dc[] = { 1,  2, 2, 1, -1, -2, -2, -1};

typedef pair<int,int> pii;


int n;
int a[maxn][maxn];
pii pos[maxn];
pii dis[maxn][maxn];
pii dp[maxn][3];

bool in(int x,int y) {
	if(x < 0 || x >= n || y < 0 || y >= n)
		return false;
	return true;
}
int get(int x,int y, int p) {
	return x*3*n+y*3+p;
}
pii operator + (const pii& p1, const pii& p2) {
	return pii(p1.x+p2.x,p1.y+p2.y);
}

int main() {
	scanf("%d", &n);
	for(int i = 0; i < n; ++i) {
		for(int j = 0; j < n; ++j) {
			scanf("%d", &a[i][j]);
			--a[i][j];
			pos[a[i][j]] = pii(i,j);
		}
	}
	for(int i = 0; i < M; ++i)
		for(int j = 0; j < M; ++j)
			dis[i][j] = pii(inf,inf);
	for(int i = 0; i < n; ++i) {
		for(int j = 0; j < n; ++j) {
			// knight
			for(int k = 0; k < 8; ++k) {
				int ni = i+dr[k], nj = j+dc[k];
				if(!in(ni,nj)) continue;
				dis[get(i,j,2)][get(ni,nj,2)] = pii(1,0);
			}
		
			// rook
			for(int k = 0; k < n; ++k) {
				if(k != j) 
					dis[get(i,j,1)][get(i,k,1)] = pii(1,0);
				if(k != i)
					dis[get(i,j,1)][get(k,j,1)] = pii(1,0);
			}
			// bugt 
			for(int k = 1; k < n; ++k) {
				int ni  = i+k, nj = j+k;
				if(in(ni,nj)) dis[get(i,j,0)][get(ni,nj,0)] = pii(1,0);
				ni = i-k, nj = j-k;
				if(in(ni,nj)) dis[get(i,j,0)][get(ni,nj,0)] = pii(1,0);
				ni = i+k, nj = j-k;
				if(in(ni,nj)) dis[get(i,j,0)][get(ni,nj,0)] = pii(1,0);
				ni = i-k, nj = j+k;
				if(in(ni,nj)) dis[get(i,j,0)][get(ni,nj,0)] = pii(1,0);
			}
			// 
			for(int k = 0; k < 3; ++k)
				for(int l = 0; l < 3; ++l) {
					if(l != k)
						dis[get(i,j,k)][get(i,j,l)] = pii(1,1);
				}
		}
	}
	for(int k = 0; k < M; ++k)
		for(int i = 0; i < M; ++i)
			for(int j = 0; j < M; ++j) {
				dis[i][j] = min(dis[i][j], dis[i][k]+dis[k][j]);
			}
	/*
	for(int i = 0; i < M; ++i)
		for(int j = 0; j < M; ++j)
			printf("%d %d %d\n", i, j, dis[i][j].x);
	*/
	for(int i = 0; i < n*n; ++i)
		for(int k = 0; k < 3; ++k)
			dp[i][k] = pii(inf,inf);
	for(int k = 0; k < 3; ++k)
		dp[0][k] = pii(0,0);
	
	for(int i = 0; i < n*n-1; ++i) {
		for(int k = 0; k < 3; ++k) {
			for(int l = 0; l < 3; ++l) {

				dp[i+1][k] = min(dp[i+1][k], dp[i][l]+dis[get(pos[i].x,pos[i].y,l)][get(pos[i+1].x,pos[i+1].y,k)]);
			}
		}
	}
	pii ans(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/Link_Ray/article/details/83338765