基于线性代数的一般图匹配

不想学带花树,于是乎就学了一发高斯消元版的一般图匹配……

这个东西的优点肯定是有的,最主要的是不用去学习带花树的那一套理论了,只需要会用高斯消元就行,代码难度相比带花树来说小一些。当然缺点也有,最要命的就是常数太大,不卡一下常都过不了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 }
View Code

注意,直接写的话会在第17个测试点TLE,这时就需要加上一些卡常技巧:在乘法之前判断乘数是否为0,以及消元时乘法之后不要取模,减完之后再一起取模,可以减少一次取模。

目前为止只写过板子(并且写得也不熟……),其他题还没做过,还是需要多加练习……

posted @ 2017-05-25 17:14  AntiLeaf  阅读(896)  评论(9编辑  收藏  举报