正题
题目链接:https://www.luogu.com.cn/problem/P4100
题目大意
给出nnn个线性无关的向量AiA_iAi,然后给出nnn个向量BiB_iBi,求一个字典序最小的排列ppp使得将任意的AiA_iAi替换为BpiB_{p_i}Bpi后依旧线性无关。
1≤n≤3001\leq n\leq 3001≤n≤300
解题思路
首先因为我们有nnn个向量AAA线性无关,那么显然这nnn个向量能表示任意向量,如果对于一个BpiB_{p_i}Bpi替换为AiA_iAi后依旧线性无关,那么BpiB_{p_i}Bpi与AiA_iAi是等价的(因为BpiB_{p_i}Bpi和AiA_iAi都代表了剩下n−1n-1n−1个无法表示的部分)。
所以只需考虑每个BjB_jBj能否换到AiA_iAi即可,构建出矩阵A=[A1,A2...An]A=[A_1,A_2...A_n]A=[A1,A2...An]和B=[B1,B2...Bn]B=[B_1,B_2...B_n]B=[B1,B2...Bn],考虑一个置换矩阵使得AR=BAR=BAR=B,那么就是对于每个BBB如何用AAA进行表示。
那么如果Ri,j=0R_{i,j}=0Ri,j=0也就是说BBB可以用AjA_jAj以外的其他AAA表示所以BBB替换到AjA_jAj之后肯定线性有关了,所以不行。
R=BAR=\frac{B}{A}R=AB,求逆得到RRR,这样我们就知道哪些AAA可以替换哪些BBB了,问题就变成了最小字典序匹配。对于这个问题我们可以考虑找一条增广环就好了。
时间复杂度O(n3)O(n^3)O(n3)
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int N=310;
const double eps=1e-8;
int n,v[N],link[N];
double A[N][N],B[N][N];
bool GetInv(){for(int i=1;i<=n;i++){int z=i;for(int j=i+1;j<=n;j++)if(fabs(A[j][i])>fabs(A[z][i]))z=j;swap(A[i],A[z]);swap(B[i],B[z]);double x=A[i][i];if(fabs(x)<eps)return 0;for(int j=1;j<=n;j++)A[i][j]/=x,B[i][j]/=x; for(int j=1;j<=n;j++){if(i==j)continue;double rate=-A[j][i];for(int k=1;k<=n;k++)A[j][k]+=rate*A[i][k],B[j][k]+=rate*B[i][k];}}for(int i=n;i>=1;i--)for(int j=1;j<i;j++){double rate=-A[j][i];for(int k=1;k<=n;k++)A[j][k]+=rate*A[i][k],B[j][k]+=rate*B[i][k]; }return 1;
}
bool dfs(int x){for(int i=1;i<=n;i++)if(!v[i]&&fabs(B[x][i])>=eps){v[i]=1;if(!link[i]||dfs(link[i])){link[i]=x;return 1;}}return 0;
}
int calc(int x,int top){for(int i=1;i<=n;i++)if(!v[i]&&fabs(B[x][i])>=eps){v[i]=1;if(link[i]==top||(link[i]>top&&calc(link[i],top))){link[i]=x;return i;}}return 0;
}
int main()
{scanf("%d",&n);for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)scanf("%lf",&A[j][i]);for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)scanf("%lf",&B[j][i]);if(!GetInv())return puts("NIE")&0;for(int i=1;i<=n;i++){memset(v,0,sizeof(v));if(!dfs(i))return puts("NIE")&0;}puts("TAK");for(int i=1;i<=n;i++){memset(v,0,sizeof(v));printf("%d\n",calc(i,i));}return 0;
}