luoguP6291 [eJOI2017]骆驼 题解

题面点这里

$ 20 $ 分思路:爆搜即可。

满分做法:

稍微手算一下就发现是没有无解的。

令 $ m = {n \over 5} $

一看 $ n \leq 1000 $ ,就知道搜索是会超时的。我们可以想到用构造矩阵的思想来解决这道题。题目说 $ n $ 是 $ 5 $ 的倍数,可以尝试构造 $ 5 \times 5$ 的矩阵拼合成答案矩阵来解决。

先假设能构造这样的 $ 5 \times 5$ 矩阵可以实现向上走,向左走,向右走,向下走,不妨来想这样一个问题:对于一个 $ m \times m $ 的格子图,从坐标为 $ (1,1) $ 的格子出发,每次可以向上,向左,向右,向下走,问怎么走才能不重不漏地走过每一个格子并最终回到起点。

当 $ m $ 为偶数时,显然可以这样走(以 $ m = 4 $ 为例):

(图略丑,不要见怪)

当 $ m $ 为奇数时,如果只满足以上操作则无解,但如果可以从 $ (2,2) $ 的格子直接到 $ (1,1) $ 的格子,就可以满足(至于如何实现等一下会讲)(以 $ m = 5 $ 为例):

接下来的问题就是如何构造 $ 5 \times 5 $ 矩阵了。

我们可以发现,对于一个矩阵,它正中心的格子连接其内部格子的数量有四格,而其它仅有三格。所以我们可以以中心点为连接其它矩阵的进行转移,如下(红色为矩阵内部的起点,蓝色为终点,以向左和向上为例,向右和向下同理):

对于 $ m $ 为奇数,可以如下处理(绿色为中转点):

对于 $ m $ 为偶数,直接从右往左走即可:

至于如何在一个 $ 5 \times 5 $ 的矩阵中做到,可以手玩特判(如果不想手玩可以看代码)

#include <cstdio>
#include <cstdlib>
#define re register
#define xia 3
#define shang 4
#define zuo 5
#define you 6//意思是拼音
using namespace std;
const int MAXN=1e3+1;
const int mat[7][5][5]={
	{
        {1,8,16,2,7},
        {11,19,5,10,18},
        {22,14,0,21,15},
        {4,9,17,3,6},
        {12,20,23,13,0}//0为中转点
    },
    {
        {22,3,9,23,2},
        {16,25,20,17,7},
        {10,13,1,4,12},
        {21,18,8,24,19},
        {15,5,11,14,6}
    },
    {
        {1,12,5,2,13},
        {7,18,15,10,19},
        {23,3,0,22,4},
        {16,11,6,17,14},
        {8,21,24,9,20}//同上
    },
    {
        {6,22,14,7,2},
        {19,9,4,20,12},
        {24,16,1,23,15},
        {5,21,13,8,3},
        {18,10,25,17,11}
    },//向下
    {
        {9,22,25,8,2},
        {17,12,4,20,15},
        {24,7,1,23,6},
        {10,21,16,11,3},
        {18,13,5,19,14}
    },//向上
    {
        {9,17,24,8,2},
        {22,12,4,19,13},
        {25,7,1,16,6},
        {10,18,23,11,3},
        {21,15,5,20,14}
    },//向左
    {
        {22,14,7,23,2},
        {9,19,4,12,18},
        {6,24,1,15,25},
        {21,13,8,20,3},
        {10,16,5,11,17}
    }//向右
};//0、1矩阵处理奇数情况,2矩阵处理偶数情况
//以上为构造的矩阵
int n,nn;
int ans[MAXN][MAXN];
struct node{
	int id,add;
	inline void up(int x,int y){
		for(re int i=1;i<=5;i++)
			for(re int j=1;j<=5;j++)
				ans[i+x][j+y]=mat[id][i-1][j-1]+add;
	}//具体来算步数
}b[201][201];
inline void calc(int x,int y,int val){
	while(b[x][y].id && b[x][y].id!=1 && b[x][y].id!=2){
		b[x][y].add=val;
		val+=25;
		if(b[x][y].id==xia) x++;
		else if(b[x][y].id==shang) x--;
		else if(b[x][y].id==zuo) y--;
		else if(b[x][y].id==you) y++;
	}
	if(b[x][y].id==1) b[x][y].add=val;
}//算每个矩阵到达前的步数
int main(){
	scanf("%d",&n),nn=n/5;
	if(n==5)return puts("1 14 7 4 15\n9 22 17 12 23\n19 5 25 20 6\n2 13 8 3 16\n10 21 18 11 24"),0;//特判
	if(nn&1){
		b[1][1].id=0,b[2][2].id=1;
		for(re int i=2;i<=nn;i++){
			if(i&1) b[1][i].id=zuo;
			else b[1][i].id=xia;
		}
		for(re int i=3;i<=nn;i++){
			if(i&1) b[2][i].id=shang;
			else b[2][i].id=zuo;
		}
		for(re int i=2;i<nn;i++)
			b[i][1].id=xia;
		b[nn][1].id=you;
		for(re int i=3;i<=nn;i++)
			if(i&1){
				for(re int j=2;j<nn;j++)
					b[i][j].id=you;
				b[i][nn].id=shang;
			}else{
				for(re int j=3;j<=nn;j++)
					b[i][j].id=zuo;
				b[i][2].id=shang;
			}
		calc(2,1,23);//奇数有两个0,所以为25-2
	}
    else{
		b[1][1].id=2;
		for(re int i=2;i<nn;i++)
			b[i][1].id=xia;
		b[nn][1].id=you;
		for(re int i=2;i<=nn;i++)
			b[1][i].id=zuo;
		for(re int i=2;i<=nn;i++)
			if(i&1){
				for(re int j=3;j<=nn;j++)
					b[i][j].id=zuo;
				b[i][2].id=shang;
			}else{
				for(re int j=2;j<nn;j++)
					b[i][j].id=you;
				b[i][nn].id=shang;
			}
		calc(2,1,24);//偶数为25-1
	}
	for(re int i=1;i<=nn;i++)
		for(re int j=1;j<=nn;j++)
			b[i][j].up((i-1)*5,(j-1)*5);
	if(nn&1) ans[5][5]=n*n-1;
	ans[3][3]=n*n;
	for(re int i=1;i<=n;i++,puts(""))
		for(re int j=1;j<=n;j++){
			printf("%d ",ans[i][j]);
		}
	return 0;
}
posted @ 2020-12-03 21:53  b1ts  阅读(91)  评论(0编辑  收藏  举报