agc037d Sorting a Grid

给出一个 \(n\)\(m\) 列的矩阵,每个格子里面的值分别为 \(a_{i,j}\),保证值不重复。

现在可以进行三波操作。

  1. 对每一行,随意重排行里面的数。
  2. 对每一列,随意重排列里面的数。
  3. 对每一行,随意重排行里面的数。

\(1\leq n,m\leq 100, 1\leq a_{i,j}\leq nm\)

对于在位置 \((i,j)\) 的值,最重要到位置 \((x,y)\) ,这是固定的。观察轨迹,发现移动方式是 \((i,j) \rightarrow (i,k)\rightarrow (x,k)\rightarrow (x,y)\).

可以考虑这样一张图,左边一列点 \(i\),其中 \(1\leq i\leq n\);右边一列点 \(i'\), \(1\leq i'\leq n\).

对于每个 \((i,j)\),找到对应 \((x,y)\) 后, 由 \(i\)\(x'\) 连边(无向)。

这样就发现这是一个 \(m\) 染色的问题,要给边由 \(1\)\(m\) 染色,使得每个点相邻的边颜色都互不相同。

根据 hall 定理可以得到。这样的一个染色问题,每种颜色的边集合是一个完美匹配。

此时就可以通过网络流来解决这个问题了。跑 \(m\) 次网络流,找 \(m\) 次完美匹配。

时间复杂度: 我不会分析

空间复杂度: \(O(nm)\)

#include<bits/stdc++.h>
using namespace std;
#define pb push_back
const int N=1e4+5;
const int inf=1e9;
struct edge{int to,cap,rev,id;};
vector<edge>g[N];int itr[N];
inline void ae(int u,int v,int w,int id){
    g[u].pb((edge){v,w,(int)g[v].size(),id});
    g[v].pb((edge){u,0,(int)g[u].size()-1,-1});
}
queue<int>q;int lv[N];
bool bfs(int s,int t){
    memset(lv,-1,sizeof(lv));
    lv[s]=0;q.push(s);
    while(!q.empty()){
        int x=q.front();q.pop();
        for(auto e:g[x]){
            int to=e.to,cap=e.cap;
            if(lv[to]==-1&&cap){
                lv[to]=lv[x]+1;
                q.push(to);
            }
        }
    }
    // for(int i=1;i<=n;i++)cerr<<lv[i]<<" ";cerr<<endl;
    return lv[t]!=-1;
}
int dfs(int x,int t,int flow){
    // cerr<<x<<" "<<t<<" "<<flow<<endl;
    if(x==t)return flow;
    for(int&i=itr[x];i<(int)g[x].size();i++){
        edge&e=g[x][i];
        if(lv[e.to]>lv[x]&&e.cap){
            int d=dfs(e.to,t,min(e.cap,flow));
            if(d){
                e.cap-=d;
                g[e.to][e.rev].cap+=d;
                return d;
            }
        }
    }
    return 0;
}
int get_flow(int s,int t){
    int res=0;
    while(bfs(s,t)){
        memset(itr,0,sizeof(itr));
        int d;
        while(d=dfs(s,t,inf))res+=d;
        res+=d;
    }
    return res;
}
int n,m;
int a[105][105],b[105][105],c[105][105];
signed main(){
    scanf("%d%d",&n,&m);
    for(int i=0;i<n;i++)for(int j=0;j<m;j++)scanf("%d",&a[i][j]);
	for(int i=0;i<n;i++)for(int j=0;j<m;j++)a[i][j]--;
	for(int i=0;i<n;i++)for(int j=0;j<m;j++){
		int x=a[i][j]/m,y=a[i][j]%m;
		ae(i,x+n,1,i*m+j);
	}
	for(int i=0;i<n;i++){
		ae(2*n,i,1,-1);
		ae(i+n,2*n+1,1,-1);
	}
	for(int t=0;t<m;t++){
		int res=get_flow(2*n,2*n+1);
		assert(res==n);
//		cerr<<"t="<<t+1<<endl;
		for(int i=0;i<n;i++)for(auto&e:g[i]){
			int to=e.to,cap=e.cap,rev=e.rev,id=e.id;
			if(n<=to&&to<2*n&&cap==0&&id!=-1){
				int j=id%m,x=a[i][j]/m,y=a[i][j]%m;
//				cerr<<i+1<<" "<<j+1<<"\n";
				b[i][t]=a[i][j];
				c[x][t]=a[i][j];
				g[to][rev].cap=0;
				e.id=-1;
			}
		}
		for(auto&e:g[2*n]){
			int to=e.to,cap=e.cap,rev=e.rev;
			if(to<n&&cap==0){
				e.cap=1;
				g[to][rev].cap=0;
			}
		}
		for(auto&e:g[2*n+1]){
			int to=e.to,cap=e.cap,rev=e.rev;
			if(n<=to&&to<2*n&&cap==1){
				e.cap=0;
				g[to][rev].cap=1;
			}
		}
	}
	for(int i=0;i<n;i++){
		for(int j=0;j<m;j++)printf("%d ",b[i][j]+1);
		printf("\n");
	}
	for(int i=0;i<n;i++){
		for(int j=0;j<m;j++)printf("%d ",c[i][j]+1);
		printf("\n");
	} 
    return 0;
}

写完才发现我 5 年前做过这个题目,真是震惊了。

posted @ 2026-05-18 17:30  xyangh  阅读(11)  评论(0)    收藏  举报