题解:有这样一个算法:
给定n个排列Ai和n个排列Bi,求一个排列p使得:
∀
i
,
j
∈
[
1
,
n
]
,
j
≠
p
i
,
A
i
,
p
i
<
A
i
,
j
∨
B
k
∣
p
k
=
j
,
j
<
B
i
,
j
\forall i,j\in[1,n],j\neq p_i,A_{i,p_i}<A_{i,j}\vee B_{k|_{p_k=j},j}<B_{i,j}
∀i,j∈[1,n],j̸=pi,Ai,pi<Ai,j∨Bk∣pk=j,j<Bi,j
可以在
O
(
n
2
)
O\left(n^2\right)
O(n2)时间内求出一组解。可以证明必定有解。
算法大致是,初始将所有数字push进队列。
对于队首元素
x
x
x,尝试
x
x
x没有尝试过的并且
A
x
,
y
A_{x,y}
Ax,y最小的
y
y
y。
如果
y
y
y没有被选择,则令
p
x
=
y
p_x=y
px=y并退出对
x
x
x的尝试;
否则若
B
z
∣
p
z
=
y
,
y
>
B
x
,
y
B_{z|_{p_z=y},y}>B_{x,y}
Bz∣pz=y,y>Bx,y,则令
p
x
=
y
p_x=y
px=y,并且将
z
∣
p
z
=
y
z|_{p_z=y}
z∣pz=y放进队尾并退出对
x
x
x的尝试;
否则对
x
x
x继续尝试下一个
y
y
y。
首先不难发现其算法复杂度由于对于x的选择越来越劣因此复杂度是
O
(
n
2
)
O\left(n^2\right)
O(n2)。同时正确性略(大雾)。
在这个题中,对行和颜色考虑,每行按照从左到右偏爱颜色,颜色按照出现的列从右向左偏向行。
然后直接运行上述算法即可。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
#define N 210
#define gc getchar()
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
inline int inn()
{
int x,ch;while((ch=gc)<'0'||ch>'9');
x=ch^'0';while((ch=gc)>='0'&&ch<='9')
x=(x<<1)+(x<<3)+(ch^'0');return x;
}
int pfx[N][N],pfy[N][N],a[N][N<<1],cntx[N],pt[N];
queue<int> q;int p[N],fr[N],vis[N],cnty[N];
int main()
{
for(int T=inn();T;T--)
{
int n=inn(),m=inn(),c;memset(cntx,0,sizeof(int)*(n+1));
rep(i,1,n) cnty[i]=n;memset(pt,0,sizeof(int)*(n+1));
memset(vis,0,sizeof(int)*(n+1));rep(i,1,n) rep(j,1,m) a[i][j]=inn();
rep(i,1,n) rep(j,1,m) if((c=a[i][j])) pfx[i][++cntx[i]]=c;
rep(j,1,m) rep(i,1,n) if((c=a[i][j])) pfy[c][i]=cnty[c]--;
while(!q.empty()) q.pop();rep(i,1,n) q.push(i);
for(int x,y;!q.empty();q.pop())
for(x=q.front(),p[x]=0;!p[x];)
if(!vis[y=pfx[x][++pt[x]]]) p[x]=y,fr[y]=x,vis[y]=1;
else if(pfy[y][x]<pfy[y][fr[y]]) p[x]=y,q.push(fr[y]),fr[y]=x;
rep(i,1,n) printf("%d ",p[i]);printf("\n");
}
return 0;
}