1. 程式人生 > >「學習筆記」uoj #41 【清華集訓2014】矩陣變換

「學習筆記」uoj #41 【清華集訓2014】矩陣變換

題解:有這樣一個演算法: 給定n個排列Ai和n個排列Bi,求一個排列p使得: i,j[1,n],jpi,Ai,pi<Ai,jBkpk=j,j<Bi,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} 可以在O(n2)O\left(n^2\right)時間內求出一組解。可以證明必定有解。 演算法大致是,初始將所有數字push進佇列。 對於隊首元素x

x,嘗試xx沒有嘗試過的並且Ax,yA_{x,y}最小的yy。 如果yy沒有被選擇,則令px=yp_x=y並退出對xx的嘗試; 否則若Bzpz=y,y>Bx,yB_{z|_{p_z=y},y}>B_{x,y},則令px=yp_x=y,並且將zpz=yz|_{p_z=y}放進隊尾並退出對xx的嘗試; 否則對xx繼續嘗試下一個yy。 首先不難發現其演算法複雜度由於對於x的選擇越來越劣因此複雜度是O(n2)O\left(n^2\right)。同時正確性略(大霧)。 在這個題中,對行和顏色考慮,每行按照從左到右偏愛顏色,顏色按照出現的列從右向左偏向行。 然後直接執行上述演算法即可。

#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; }