【AT5161】[AGC037D] Sorting a Grid(二分图匹配)
- 给定一个\(n\times m\)的矩阵\(A\),你可以将\(A\)每一行任意重排得到\(B\),将\(B\)每一列任意重排得到\(C\),将\(C\)每一行任意重排得到\(D\)。
- 要求\(D\)的\((i,j)\)格子上的数是\((i-1)\times m+j\),求构造一组合法的\(B,C\)。
- \(n,m\le100\)
题意转化
反向考虑,要让最终\(D\)满足这样的条件,因为\(C\)到\(D\)只能对于行重排,就需要让每一行的数都正确。
又由于\(B\)到\(C\)只能对于列重排,所以\(B\)的每一列中应该同时包含每种行的元素。
所以说,给最终第\(i\)行的元素赋上一个颜色\(i\),我们的问题就变成了,对\(A\)进行行重排,让每一列都能包含所有颜色的元素。
二分图匹配
经典的二分图匹配问题,对于每个元素,从它所在的行对应的点向它的颜色对应的点连边。
显然这是一张二分图,而一对完美匹配就意味着一列的填法。
因此只要跑\(m\)次匈牙利算法就结束了。
代码:\(O(n^2m^2)\)
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 100
using namespace std;
int n,m,a[N+5][N+5],p[N+5][N+5],b[N+5][N+5],e[N+5][N+5];
int s[N+5],vis[N+5];I bool Match(CI x,CI ti) {for(RI i=1;i<=n;++i)//匈牙利算法
if(e[x][i]&&vis[i]^ti&&(vis[i]=ti,!s[i]||Match(s[i],ti))) return s[i]=x;return 0;}
int main()
{
RI i,j;for(scanf("%d%d",&n,&m),i=1;i<=n;++i) for(j=1;j<=m;++j) scanf("%d",&a[i][j]),++e[p[i][j]=(a[i][j]-1)/m+1][i];//记录每行和每种颜色间的边数
RI k;for(i=1;i<=m;++i) {for(j=1;j<=n;++j) s[j]=vis[j]=0;for(j=1;j<=n;++j) Match(j,j);//做m次二分图匹配
for(j=1;j<=n;++j) for(--e[s[j]][j],k=1;k<=m;++k) if(p[j][k]==s[j]) {b[i][j]=a[j][k],p[j][k]=0;break;}}//求出该列的填法
for(i=1;i<=n;putchar('\n'),++i) for(j=1;j<=m;++j) printf("%d ",b[j][i]);for(i=1;i<=m;++i) sort(b[i]+1,b[i]+n+1);//直接输出b,然后给每列排序
for(i=1;i<=n;putchar('\n'),++i) for(j=1;j<=m;++j) printf("%d ",b[j][i]);return 0;//输出c
}
待到再迷茫时回头望,所有脚印会发出光芒