基于线性代数的一般图匹配
不想学带花树,于是乎就学了一发高斯消元版的一般图匹配……
这个东西的优点肯定是有的,最主要的是不用去学习带花树的那一套理论了,只需要会用高斯消元就行,代码难度相比带花树来说小一些。当然缺点也有,最要命的就是常数太大,不卡一下常都过不了UOJ#79……
贴一份UOJ#79的板子,懒得解释了,不要介意……
UPD:一开始贴的板子似乎有错,重新贴一个应该没错的板子好了
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 const int maxn=505,p=1000000007; 6 void Gauss(int[][maxn],int[][maxn],int); 7 void eliminate(int,int); 8 int qpow(int,int); 9 int A[maxn][maxn],B[maxn][maxn],t[maxn][maxn],id[maxn],a[maxn]; 10 bool row[maxn]={false},col[maxn]={false}; 11 int n,m,girl[maxn]; 12 int main(){ 13 srand(23333333); 14 scanf("%d%d",&n,&m); 15 while(m--){ 16 int x,y; 17 scanf("%d%d",&x,&y); 18 A[x][y]=rand()%p; 19 A[y][x]=-A[x][y]; 20 } 21 for(int i=1;i<=n;i++)id[i]=i; 22 memcpy(t,A,sizeof(t)); 23 Gauss(A,NULL,n); 24 m=n; 25 n=0; 26 for(int i=1;i<=m;i++)if(A[id[i]][id[i]])a[++n]=i; 27 for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)A[i][j]=t[a[i]][a[j]]; 28 Gauss(A,B,n); 29 for(int i=1;i<=n;i++)if(!girl[a[i]]) 30 for(int j=i+1;j<=n;j++)if(!girl[a[j]]&&t[a[i]][a[j]]&&B[j][i]){ 31 girl[a[i]]=a[j]; 32 girl[a[j]]=a[i]; 33 eliminate(i,j); 34 eliminate(j,i); 35 break; 36 } 37 printf("%d\n",n>>1); 38 for(int i=1;i<=m;i++)printf("%d ",girl[i]); 39 return 0; 40 } 41 void Gauss(int A[][maxn],int B[][maxn],int n){ 42 if(B){ 43 memset(B,0,sizeof(t)); 44 for(int i=1;i<=n;i++)B[i][i]=1; 45 } 46 for(int i=1;i<=n;i++){ 47 if(!A[i][i]){ 48 for(int j=i+1;j<=n;j++)if(A[j][i]){ 49 swap(id[i],id[j]); 50 for(int k=i;k<=n;k++)swap(A[i][k],A[j][k]); 51 if(B)for(int k=1;k<=n;k++)swap(B[i][k],B[j][k]); 52 break; 53 } 54 if(!A[i][i])continue; 55 } 56 int inv=qpow(A[i][i],p-2); 57 for(int j=1;j<=n;j++)if(i!=j&&A[j][i]){ 58 int t=(long long)A[j][i]*inv%p; 59 for(int k=i;k<=n;k++)if(A[i][k])A[j][k]=(A[j][k]-(long long)t*A[i][k])%p; 60 if(B)for(int k=1;k<=n;k++)if(B[i][k])B[j][k]=(B[j][k]-(long long)t*B[i][k])%p; 61 } 62 } 63 if(B)for(int i=1;i<=n;i++){ 64 int inv=qpow(A[i][i],p-2); 65 for(int j=1;j<=n;j++)if(B[i][j])B[i][j]=(long long)B[i][j]*inv%p; 66 } 67 } 68 void eliminate(int r,int c){ 69 row[r]=col[c]=true; 70 int inv=qpow(B[r][c],p-2); 71 for(int i=1;i<=n;i++)if(!row[i]&&B[i][c]){ 72 int t=(long long)B[i][c]*inv%p; 73 for(int j=1;j<=n;j++)if(!col[j]&&B[r][j])B[i][j]=(B[i][j]-(long long)t*B[r][j])%p; 74 } 75 } 76 int qpow(int a,int b){ 77 int ans=1; 78 for(;b;b>>=1,a=(long long)a*a%p)if(b&1)ans=(long long)ans*a%p; 79 return ans; 80 }
注意,直接写的话会在第17个测试点TLE,这时就需要加上一些卡常技巧:在乘法之前判断乘数是否为0,以及消元时乘法之后不要取模,减完之后再一起取模,可以减少一次取模。
目前为止只写过板子(并且写得也不熟……),其他题还没做过,还是需要多加练习……
233333333