- 给定一个(n imes m)的矩阵(A),你可以将(A)每一行任意重排得到(B),将(B)每一列任意重排得到(C),将(C)每一行任意重排得到(D)。
- 要求(D)的((i,j))格子上的数是((i-1) imes m+j),求构造一组合法的(B,C)。
- (n,mle100)
题意转化
反向考虑,要让最终(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('
'),++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('
'),++i) for(j=1;j<=m;++j) printf("%d ",b[j][i]);return 0;//输出c
}