1 条题解

  • 0
    @ 2025-8-24 22:19:55

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar free_fall
    do not go gentle into that good night

    搬运于2025-08-24 22:19:55,当前版本为作者最后更新于2023-10-08 16:10:34,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    关于这道题我有一种正常的写法和一种不需要大脑的写法,但是这里空白太小,正常的写不下……(其实是楼上的大佬已经讲得很详细了)。

    于是我就来提供新的思路辣。

    对一个二维数组的行和列进行数次 shift 操作,使得它最终变成一个和谐数组。

    定义操作 move1(x,y)move1(x,y) 为将第 xx 行右移 yy 次,move2(x,y)move2(x,y) 为将第 xx 列下移 yy 次。

    对于前 n1n-1 行,我们将需要转移到 (i,j)(i,j) 的点的坐标记为 (x,y)(x,y),假设当前需要转移的数字是 99,我们可以这样转移:

    $\begin{matrix} &0 &1 &2 &3 \\ &4 &5 &6 &7 \\ &8 &? &? &? \\ &? &? &9 &? \end{matrix}$

    move2(j,x-i):

    $\begin{matrix} &0 &? &2 &3 \\ &4 &1 &6 &7 \\ &8 &5 &? &? \\ &? &? &9 &? \end{matrix}$

    move1(x,(j-y+m)%m):

    $\begin{matrix} &0 &? &2 &3 \\ &4 &1 &6 &7 \\ &8 &5 &? &? \\ &? &9 &? &? \end{matrix}$

    move2(j,i):

    $\begin{matrix} &0 &1 &2 &3 \\ &4 &5 &6 &7 \\ &8 &9 &? &? \\ &? &? &? &? \end{matrix}$

    如果非常不巧,我们需要移的数和目标位置在同一行,只先需这样:

    move2(y,1);
    move1(x+1,1);
    move2(y,n-1);
    x++,y=y%m+1;
    

    做完上述操作后,如果需要移的数和目标位置在同一列,再这样:

    move1(x,1);
    y=y%m+1;
    

    最后做一遍上面带图的操作即可。至此,我们已经完成了前 n1n-1 行,只有最后一行是处于无序的状态。上面的操作是将第 i+1i+1 行作为中转站来移动,但是最后一行我们显然不可以这么做,于是我们将 (n1,m)(n-1,m) 这个点作为中转站转移,这里我们枚举的 ii 是当前目标的列数,目标为 (n,i)(n,i),现在我要将 13 移到对应位置,看起来大概是这样的:

    $\begin{matrix} &0 &1 &2 &3 \\ &4 &5 &6 &7 \\ &8 &9 &10 &11 \\ &12 &? &13 &? \end{matrix}$

    move2(m,1):

    $\begin{matrix} &0 &1 &2 &? \\ &4 &5 &6 &3 \\ &8 &9 &10 &7 \\ &12 &? &13 &11 \end{matrix}$

    move1(n,m-y):

    $\begin{matrix} &0 &1 &2 &? \\ &4 &5 &6 &3 \\ &8 &9 &10 &7 \\ &11 &12 &? &13 \end{matrix}$

    move2(m,n-1):

    $\begin{matrix} &0 &1 &2 &3 \\ &4 &5 &6 &7 \\ &8 &9 &10 &13 \\ &11 &12 &? &? \end{matrix}$

    move1(n,y):

    $\begin{matrix} &0 &1 &2 &3 \\ &4 &5 &6 &7 \\ &8 &9 &10 &13 \\ &12 &? &? &11 \end{matrix}$

    move1(n,m-i):

    $\begin{matrix} &0 &1 &2 &3 \\ &4 &5 &6 &7 \\ &8 &9 &10 &13 \\ &? &11 &12 &? \end{matrix}$

    move2(m,1):

    $\begin{matrix} &0 &1 &2 &? \\ &4 &5 &6 &3 \\ &8 &9 &10 &7 \\ &? &11 &12 &13 \end{matrix}$

    move1(n,i):

    $\begin{matrix} &0 &1 &2 &? \\ &4 &5 &6 &3 \\ &8 &9 &10 &7 \\ &12 &13 &? &11 \end{matrix}$

    move2(m,n-1):

    $\begin{matrix} &0 &1 &2 &3 \\ &4 &5 &6 &7 \\ &8 &9 &10 &11 \\ &12 &13 &? &? \end{matrix}$

    这样最后一列就解决了,到这里为止,我们几乎没有用到大脑就完成了……吗?其实有可能在你做完这些操作的时候最后的数组是长这样的:

    $\begin{matrix} &0 &1 &2 &3 \\ &4 &5 &6 &7 \\ &8 &9 &10 &15 \\ &12 &13 &14 &11 \end{matrix}$

    看,这个 11111515 的位置反了,解决这一步是非常难想的,然而这篇题解就是要教你怎么绕开这种情况。简单来说,最后你做到这一步出现这种情况的概率接近二分之一,我们只需要充分发扬人类智慧,每次做完上述操作之后判断是否和谐,如果不和谐就随机打乱,重新再做一次。

    看起来很玄学,但是这个代码跑出正解的的期望次数是 22,因为是常数可以忽略,最终时间复杂度依然是 O(n3)O(n^3),明显能过。

    但是这里还有一些细节上的问题,比如这个 movemove 函数中的 yy 有可能是负数,在这里的 spj 上判为错误,所以要加上 nn 或者 mm 取模,具体实现见代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int N=105,M=1e5+5;
    int n,m,a[N][N],b[N],mark[N][N],id[N*N],top;
    bool flag;
    struct op{
    	int op,x,y;
    }st[M];
    int rand(int l,int r){
    	return rand()%(r-l+1)+l;
    }
    void move1(int x,int y){
    	if(y%m==0)return;
    	y=(y%m+m)%m;
    	st[++top]={1,x,y};
    	for(int i=1;i<=m;i++){
    		b[(i+y-1)%m+1]=a[x][i];
    	}
    	for(int i=1;i<=m;i++){
    		a[x][i]=b[i];
    		id[a[x][i]]=(x-1)*m+i;
    	}
    	return;
    }
    void move2(int x,int y){
    	if(y%n==0)return;
    	y=(y%n+n)%n;
    	st[++top]={2,x,y};
    	for(int i=1;i<=n;i++){
    		b[(i+y-1)%n+1]=a[i][x];
    	}
    	for(int i=1;i<=n;i++){
    		a[i][x]=b[i];
    		id[a[i][x]]=(i-1)*m+x;
    	}
    	return;
    }
    bool check(){
    	for(int i=1;i<=n;i++){
    		for(int j=1;j<=m;j++){
    			if(a[i][j]!=(i-1)*m+j-1)return false;
    		}
    	}
    	return true;
    }
    int main(){
    	srand(time(0));
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;i++){
    		for(int j=1;j<=m;j++){
    			scanf("%d",&a[i][j]);
    			mark[i][j]=a[i][j];
    			id[a[i][j]]=(i-1)*m+j;
    		}
    	}
    	while(!check()){
    		top=0;
    		for(int i=1;i<=n;i++){
    			for(int j=1;j<=m;j++){
    				a[i][j]=mark[i][j];
    				id[a[i][j]]=(i-1)*m+j;
    			}
    		}
    		for(int i=1;i<=n;i++){
    			int op=rand(1,2);
    			if(op==1)move1(rand(1,n),rand(1,m-1));
    			if(op==2)move2(rand(1,m),rand(1,n-1));
    		}
    		for(int i=1;i<n;i++){
    			for(int j=1;j<=m;j++){
    				int x=(id[(i-1)*m+j-1]-1)/m+1,y=(id[(i-1)*m+j-1]-1)%m+1;
    				if(x==i&&y==j)continue;
    				if(i==x){
    					move2(y,1);
    					move1(x+1,1);
    					move2(y,n-1);
    					x++,y=y%m+1;
    				}
    				if(j==y){
    					move1(x,1);
    					y=y%m+1;
    				}
    				move2(j,x-i);
    				move1(x,(j-y+m)%m);
    				move2(j,n-x+i);
    			}
    		}
    		for(int i=1;i<=m;i++){
    			int x=(id[(n-1)*m+i-1]-1)/m+1,y=(id[(n-1)*m+i-1]-1)%m+1,flag=y==m;
    			if(x==n&&y==i)continue;
    			if(x==n-1)continue;
    			if(flag){
    				move1(n,1);
    				y=y%m+1;
    			}
    			move2(m,1),move1(n,m-y);
    			move2(m,n-1),move1(n,y);
    			if(flag)move1(n,m-1);
    			move1(n,m-i),move2(m,1);
    			move1(n,i),move2(m,n-1);
    		}
    		int x=(id[(n-1)*m-1]-1)/m+1,y=(id[(n-1)*m-1]-1)%m+1;
    		if(x!=n-1||y!=m){
    			move2(m,1),move1(n,m-y);
    			move2(m,n-1),move1(n,y);
    		}
    	}
    	printf("%d\n",top);
    	for(int i=1;i<=top;i++){
    		printf("%d %d %d\n",st[i].op,st[i].x,st[i].y);
    	}
    	return 0;
    }
    

    其实这个对换两个数的操作可以稍微修改一下,但是思路过于复杂,这里就随便贴一下代码满足强迫症患者的需求吧:

    #include<bits/stdc++.h>
    using namespace std;
    const int N=105,M=1e5+5;
    int n,m,a[N][N],b[N],mark[N][N],id[N*N],top,cnt;
    struct op{
    	int op,x,y;
    }st[M];
    void move1(int x,int y){
    	if(y%m==0)return;
    	y=(y%m+m)%m;
    	st[++top]={1,x,y};
    	for(int i=1;i<=m;i++){
    		b[(i+y-1)%m+1]=a[x][i];
    	}
    	for(int i=1;i<=m;i++){
    		a[x][i]=b[i];
    		id[a[x][i]]=(x-1)*m+i;
    	}
    	return;
    }
    void move2(int x,int y){
    	if(y%n==0)return;
    	y=(y%n+n)%n;
    	st[++top]={2,x,y};
    	for(int i=1;i<=n;i++){
    		b[(i+y-1)%n+1]=a[i][x];
    	}
    	for(int i=1;i<=n;i++){
    		a[i][x]=b[i];
    		id[a[i][x]]=(i-1)*m+x;
    	}
    	return;
    }
    int main(){
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;i++){
    		for(int j=1;j<=m;j++){
    			scanf("%d",&a[i][j]);
    			mark[i][j]=a[i][j];
    			id[a[i][j]]=(i-1)*m+j;
    		}
    	}
    	for(int i=1;i<n;i++){
    		for(int j=1;j<=m;j++){
    			int x=(id[(i-1)*m+j-1]-1)/m+1,y=(id[(i-1)*m+j-1]-1)%m+1;
    			if(x==i&&y==j)continue;
    			if(i==x){
    				move2(y,1);
    				move1(x+1,1);
    				move2(y,n-1);
    				x++,y=y%m+1;
    			}
    			if(j==y){
    				move1(x,1);
    				y=y%m+1;
    			}
    			move2(j,x-i);
    			move1(x,(j-y+m)%m);
    			move2(j,n-x+i);
    		}
    	}
    	for(int i=1;i<m;i++){
    		int x=(id[(n-1)*m+i-1]-1)/m+1,y=(id[(n-1)*m+i-1]-1)%m+1;
    		if(y==i)continue;
    		move1(n,m-y);
    		if(cnt&1)move2(m,1);
    		else move2(m,n-1);
    		move1(n,y);
    		move1(n,m-i);
    		if(cnt&1)move2(m,n-1);
    		else move2(m,1);
    		move1(n,i);
    		move1(n,m-y);
    		if(cnt&1)move2(m,1);
    		else move2(m,n-1);
    		move1(n,y);
    		cnt++;
    	}
    	if(a[1][m]!=m-1){
    		for(int i=1;i<n;i++){
    			if(i&1)move1(1,m-1);
    			else move1(1,1);
    			move2(m,n-1);
    		}
    		move2(m,n-1);
    		if(n&1)move1(1,m-1);
    		else move1(1,1);
    	}
    	printf("%d\n",top);
    	for(int i=1;i<=top;i++){
    		printf("%d %d %d\n",st[i].op,st[i].x,st[i].y);
    	}
    	return 0;
    }
    
    • 1

    信息

    ID
    5367
    时间
    2000ms
    内存
    256MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者