杭电多校第八场,心血来潮想写题解,因为自己的方法好像和官方题解的不一样,特地上来分享一下。
题意:给一张n*m的方格图,求输出一条曼哈顿路径,要求路径上两点的欧几里得距离严格大于1,严格小于3。
1.大体情况:一行一列的方格图是无解的,但1*1的方格图有解,2*2的方格图也是无解的。其余所有情况均有解。
2.主体思路:一开始看错题目,以为是小于等于3,这样我们必然可以找到一个路径,从最底行一行一行走上去。而严格小于3,我们可以两行两行的走上去。
3.考虑最小的横向合法方格图(纵向同理),则有2*3、2*4方格图,所有的2*n的方格图,均可以根据奇偶关系,从起点移动到上述方格图中,再从上述方格图中出来,回到起点附近的一个点,完成合法的曼哈顿路径。换句话说,对于任意2行/列方格图,我们均可以构造一个合法路径,使得从第1列/行出发,再回到第1列/行。
4.具体构造:从(1,1)出发,斜着走"田"字,直到该两行末尾的2*3(奇数列)、2*4(偶数列)方格图,通过事先构造好的最小合法路径,再走"田"字出来,回到(2,1)。注意此时要往3、4行走,但(2,1)不能走到(3,1),只能走到(4,1),所以接下来进入行末尾的方格图的路径会不同,要准备两个最小合法路径。
5.通过上述构造方法:2行/列的走,我们可以解决所有的偶*偶、奇*偶、偶*偶方格图,考虑奇*奇的情况。 在一个大方格图中,把左上角一个3*3矩阵抠掉,则下方(n-3)*m的方格图是一个偶*奇的,最上面三行的剩下部分,则是若干个3*2的方格图,这样就可以转换成先前的情况了,当然我们要预处理一个3*3的合法路径。
黑体部分是我的核心想法,具体操作都在代码里,预先构造路径那块需要考虑很多细节,因为要确保移动的距离严格小于3,写起来也比较麻烦,就不细说了。现场因为看错题浪费了一个小时,剩下一个半小时没搞出来,还是比较可惜吧…
#include<bits/stdc++.h>
using namespace std;
int o24x[10] = {0,1,1,2,2,1,2,1,2}; //2*4矩阵的两种走法
int o24y[10] = {0,1,3,4,2,4,3,2,1};
int t24x[10] = {0,2,1,2,1,2,2,1,1};
int t24y[10] = {0,1,2,3,4,2,4,3,1};
int o23x[8] = {0,1,2,1,2,1,2}; //2*3矩阵的两种走法
int o23y[8] = {0,1,3,2,1,3,2};
int t23x[8] = {0,2,1,2,1,1,2};
int t23y[8] = {0,1,2,3,1,3,2};
int o32x[8] = {0,1,3,2,1,3,2}; //3*2矩阵的两种走法
int o32y[8] = {0,1,2,1,2,1,2};
int t32x[8] = {0,1,2,3,1,3,2};
int t32y[8] = {0,2,1,2,1,1,2};
int o42x[10] = {0,1,3,4,2,4,3,2,1}; //4*2矩阵的两种走法
int o42y[10] = {0,1,1,2,2,1,2,1,2};
int t42x[10] = {0,1,2,3,4,2,4,3,1};
int t42y[10] = {0,2,1,2,1,2,2,1,1};
int ok33x[10] = {0,1,1,3,2,3,2,3,1,2}; //3*3矩阵的走法
int ok33y[10] = {0,3,1,1,2,3,1,2,2,3};
int main()
{
int T;
scanf("%d",&T);
while(T--){
int n,m;
scanf("%d%d",&n,&m);
if(n == 1 && m == 1){ //特殊情况的判断
printf("YES\n1 1\n");
continue;
}
if(min(n,m) == 1 || n == 2 && m == 2){
printf("NO\n");
continue;
}
printf("YES\n");
if(n%2 == 0 && m%2 == 0 && m>=4){ //偶*偶的矩阵,先从左往右,再从下往上
int flag = 1;
for(int i = 1;i<=n;i+=2){
int res = 1;
for(int j = 1;j<=m-4;j++){
if(res == 1 && flag == 1 || res == 0 && flag == 0)
printf("%d %d\n",i,j);
else if(res == 1 && flag == 0 || res == 0 && flag == 1)
printf("%d %d\n",i+1,j);
res ^= 1;
}
if(flag == 1)
for(int j = 1;j<=8;j++)
printf("%d %d\n",i-1+o24x[j],m-4+o24y[j]);
else
for(int j = 1;j<=8;j++)
printf("%d %d\n",i-1+t24x[j],m-4+t24y[j]);
for(int j = m-4;j>=1;j--){
if(res == 1 && flag == 1 || res == 0 && flag == 0)
printf("%d %d\n",i,j);
else if(res == 1 && flag == 0 || res == 0 && flag == 1)
printf("%d %d\n",i+1,j);
res ^= 1;
}
flag ^= 1;
}
}
else if(n%2 == 0 && m%2 == 1 && m>=3){ //偶*奇的矩阵,先从左往右,再从下往上
int flag = 1;
for(int i = 1;i<=n;i+=2){
int res = 1;
for(int j = 1;j<=m-3;j++){
if(res == 1 && flag == 1 || res == 0 && flag == 0)
printf("%d %d\n",i,j);
else if(res == 1 && flag == 0 || res == 0 && flag == 1)
printf("%d %d\n",i+1,j);
res ^= 1;
}
if(flag == 1)
for(int j = 1;j<=6;j++)
printf("%d %d\n",i-1+o23x[j],m-3+o23y[j]);
else
for(int j = 1;j<=6;j++)
printf("%d %d\n",i-1+t23x[j],m-3+t23y[j]);
res ^= 1;
for(int j = m-3;j>=1;j--){
if(res == 1 && flag == 1 || res == 0 && flag == 0)
printf("%d %d\n",i+1,j);
else if(res == 1 && flag == 0 || res == 0 && flag == 1)
printf("%d %d\n",i,j);
res ^= 1;
}
flag ^= 1;
}
}
else if(m%2 == 0 && n%2 == 1){ //奇*偶的矩阵,先从下往上,再从左往右
int flag = 1;
for(int i = 1;i<=m;i+=2){
int res = 1;
for(int j = 1;j<=n-3;j++){
if(flag == 1 && res == 1 || flag == 0 && res == 0)
printf("%d %d\n",j,i);
else
printf("%d %d\n",j,i+1);
res ^= 1;
}
if(flag == 1)
for(int j = 1;j<=6;j++)
printf("%d %d\n",n-3+o32x[j],i-1+o32y[j]);
else
for(int j = 1;j<=6;j++)
printf("%d %d\n",n-3+t32x[j],i-1+t32y[j]);
for(int j = n-3;j>=1;j--){
if(flag == 1 && res == 1 || flag == 0 && res == 0)
printf("%d %d\n",j,i);
else
printf("%d %d\n",j,i+1);
res ^= 1;
}
flag ^= 1;
}
}
else if(m%2 == 0 && n%2 == 0){ //偶*偶的矩阵,先从下往上,再从左往右
int flag = 1;
for(int i = 1;i<=m;i+=2){
int res = 1;
for(int j = 1;j<=n-4;j++){
if(flag == 1 && res == 1 || flag == 0 && res == 0)
printf("%d %d\n",j,i);
else
printf("%d %d\n",j,i+1);
res ^= 1;
}
if(flag == 1)
for(int j = 1;j<=8;j++)
printf("%d %d\n",n-4+o42x[j],i-1+o42y[j]);
else
for(int j = 1;j<=8;j++)
printf("%d %d\n",n-4+t42x[j],i-1+t42y[j]);
for(int j = n-4;j>=1;j--){
if(flag == 1 && res == 1 || flag == 0 && res == 0)
printf("%d %d\n",j,i);
else
printf("%d %d\n",j,i+1);
res ^= 1;
}
flag ^= 1;
}
}
else{ //奇*奇的矩阵,抠掉上面三行,从左往右,从下往上
n -= 3;
int flag = 1;
for(int i = 1;i<=n;i+=2){
int res = 1;
for(int j = 1;j<=m-3;j++){
if(res == 1 && flag == 1 || res == 0 && flag == 0)
printf("%d %d\n",i,j);
else if(res == 1 && flag == 0 || res == 0 && flag == 1)
printf("%d %d\n",i+1,j);
res ^= 1;
}
if(flag == 1)
for(int j = 1;j<=6;j++)
printf("%d %d\n",i-1+o23x[j],m-3+o23y[j]);
else
for(int j = 1;j<=6;j++)
printf("%d %d\n",i-1+t23x[j],m-3+t23y[j]);
res ^= 1;
for(int j = m-3;j>=1;j--){
if(res == 1 && flag == 1 || res == 0 && flag == 0)
printf("%d %d\n",i+1,j);
else if(res == 1 && flag == 0 || res == 0 && flag == 1)
printf("%d %d\n",i,j);
res ^= 1;
}
flag ^= 1;
}
for(int i = 1;i<=9;i++) //最上面三行的特殊走法
printf("%d %d\n",n+ok33x[i],ok33y[i]);
n += 3;
for(int i = 4;i<=m;i+=2)
for(int j = 1;j<=6;j++)
printf("%d %d\n",n-3+o32x[j],i-1+o32y[j]);
}
}
return 0;
}