这题当时居然不会。。。
我们显然是要让第一次行内重排后每一列内部为排列。
考虑一个简单的算法,我们一列一列考虑,每次把权值和存在该权值的行找一个完美匹配即可。
这个是否可行呢?注意到对于还剩
d
d
d列的情况,任意取
k
k
k个不同权值,恰会出现
k
d
kd
kd次,而每一行最多只剩
d
d
d个数,因此至少会有
k
k
k行存在这些权值之一,根据Hall定理,一定存在左部图到右部图的完美匹配。
使用dinic算法的话时间复杂度为
O
(
N
M
2
N
)
\mathcal O(NM^2\sqrt N)
O(NM2N)。
#include <bits/stdc++.h>
using namespace std;
bool e[105][105],vis[105];
int link[105];
bool dfs(int x,int n) {
for(int i=1;i<=n;i++)
if (e[x][i]&&!vis[i]) {
vis[i]=1;
if (!link[i]||dfs(link[i],n)) {
link[i]=x;
return 1;
}
}
return 0;
}
void match(int n) {
memset(link,0,sizeof(link));
for(int i=1;i<=n;i++) {
memset(vis,0,sizeof(vis));
dfs(i,n);
}
}
int a[105][105],b[105][105],val[105][105];
bool use[105][105];
int main() {
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++) {
scanf("%d",&a[i][j]);
val[i][j]=(a[i][j]-1)/m+1;
}
for(int i=1;i<=m;i++) {
memset(e,0,sizeof(e));
for(int j=1;j<=n;j++)
for(int k=1;k<=m;k++)
if (!use[j][k]) e[j][val[j][k]]=1;
match(n);
for(int j=1;j<=n;j++) {
int x=link[j];
for(int k=1;k<=m;k++)
if (!use[x][k]&&val[x][k]==j) {
use[x][k]=1;
b[x][i]=a[x][k];
break;
}
}
}
for(int i=1;i<=n;i++) {
for(int j=1;j<=m;j++) printf("%d ",b[i][j]);
printf("\n");
}
for(int i=1;i<=m;i++) {
for(int j=1;j<=n;j++)
for(int k=1;k<j;k++)
if (b[j][i]<b[k][i]) swap(b[j][i],b[k][i]);
}
for(int i=1;i<=n;i++) {
for(int j=1;j<=m;j++) printf("%d ",b[i][j]);
printf("\n");
}
return 0;
}