一般图匹配

tutte矩阵

一般图的最大匹配数为tutte矩阵的秩(必为偶数)除2

求出最大匹配后加入(n-2*最大匹配数)个点与所有点连边,新图一定存在最大匹配

与新点匹配的点在原图中不与任何点匹配

求一张图的完美匹配时可尝试删除一条边,两个点看是否仍存在完美匹配

有结论两点i,j可能在最大匹配中当且仅当

A的逆矩阵[j][i]不为0且i,j有边相连

删除一条边时可以以第i行的第j列为主元,第j行的第i列为主元消元维护逆矩阵

复杂度O(n^3)常数巨大。

#pragma GCC optimize(2)
#include <cstdio>
#include <iostream>
#include <cmath>
#define LL long long
using namespace std;

  const LL mo=1e9+7;
  LL sed=12939512;

  int n,m;
  int b[2001],fin[2001];
  int sid[124751][2],cnt,edge[2001][2001];
  LL ran(){
      sed*=21409124;sed+=1249134;sed^=1234904;sed%=mo;
      return(sed);
  }

  LL qpow(LL bas,int powe){
      LL ret=1;
      for (;powe;bas*=bas,bas%=mo){
        if (powe&1) ret*=bas,ret%=mo;
      powe>>=1;    
    }
    return(ret);
  }

  struct matrix{
    LL a[1201][1201],tmp[1201][1201];
    int n,m;
    
    int getrank(){
      for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) tmp[i][j]=a[i][j];
      for (int i=1;i<=n;i++){
          for (int j=i;j<=n;j++) if (a[j][i]) {swap(a[j],a[i]);break;}
          if (!a[i][i]) continue;
          LL inv=qpow(a[i][i],mo-2);
        for (int j=i+1;j<=n;j++){
          LL bas=a[j][i]*inv%mo;
          if (!bas) continue;
          for (int k=i;k<=n;k++)
            if (a[i][k])
              a[j][k]-=a[i][k]*bas%mo,a[j][k]%=mo,a[j][k]+=mo,a[j][k]%=mo;
          }    
      }
      LL ret=0;
      for (int i=1;i<=n;i++) if (a[i][i]) ret++;
      return(ret);
    }
    
    void getinv(){
      for (int i=1;i<=n;i++) a[i][n+i]=1;
      for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) a[i][j]=(a[i][j]%mo+mo)%mo;
      for (int i=1;i<=n;i++){
        int po;LL maxi=0;
        for (int j=i;j<=n;j++){
          if (abs(a[j][i])>maxi){
            maxi=abs(a[j][i]);po=j;
          }
        }
        for (int j=1;j<=2*n;j++){
          LL t=a[i][j];a[i][j]=a[po][j];a[po][j]=t;
        }
        if (abs(maxi)==0) continue;
        
        LL rev=qpow(a[i][i],mo-2);    
        for (int j=i+1;j<=n;j++){
          LL tim=a[j][i]*rev%mo;
          for (int k=i;k<=2*n;k++) a[j][k]-=a[i][k]*tim%mo,a[j][k]%=mo;
          }
        }
        
        for (int i=1;i<=n;i++) for (int j=1;j<=2*n;j++) a[i][j]=(a[i][j]%mo+mo)%mo;
        for (int i=n;i>=1;i--){
          for (int j=i+1;j<=n;j++){
            for (int k=n+1;k<=2*n;k++)
              a[i][k]-=a[i][j]*a[j][k]%mo,a[i][k]%=mo;
            a[i][j]=0;          
          }
          for (int j=n+1;j<=2*n;j++)
            a[i][j]*=qpow(a[i][i],mo-2),a[i][j]%=mo;
          a[i][i]=1;  
        }
        
        for (int i=1;i<=n;i++)
          for (int j=1;j<=n;j++)
            a[i][j]=(a[i][j+n]%mo+mo)%mo;
    }
    
    void elim(int x,int y){
      for (int i=1;i<=n;i++)
        if (!b[i]){
          LL bas=a[i][y]*qpow(a[x][y],mo-2)%mo;
          for (int j=1;j<=n;j++){
            a[i][j]-=a[x][j]*bas%mo;
            a[i][j]%=mo;a[i][j]+=mo;a[i][j]%=mo;
          }
        }
    }
  }a;

  int main(){      
      scanf("%d%d",&n,&m);
      a.n=a.m=n;
      for (int i=1;i<=m;i++){
        int t1,t2;
        scanf("%d%d",&t1,&t2);
        sid[++cnt][0]=t1;sid[cnt][1]=t2;
      LL num=ran();
      a.a[min(t1,t2)][max(t1,t2)]=num;
      a.a[max(t1,t2)][min(t1,t2)]=mo-num;
    }
    int ans=a.getrank()/2;
    printf("%d\n",ans);
    
    int addin=n-ans*2;
    a.n=n+addin;a.m=n+addin;
    for (int i=1;i<=a.n;i++) for (int j=1;j<=a.m;j++) a.a[i][j]=0;
    for (int i=1;i<=cnt;i++){
      LL num=ran();
      a.a[min(sid[i][0],sid[i][1])][max(sid[i][0],sid[i][1])]=num;
      a.a[max(sid[i][0],sid[i][1])][min(sid[i][0],sid[i][1])]=-num;
      edge[min(sid[i][0],sid[i][1])][max(sid[i][0],sid[i][1])]=1;
      edge[max(sid[i][0],sid[i][1])][min(sid[i][0],sid[i][1])]=1;
    }
    for (int i=1;i<=addin;i++) 
      for (int j=1;j<=n;j++){
          LL num=ran();
          a.a[j][n+i]=num;
          a.a[n+i][j]=-num;
          edge[j][n+i]=edge[n+i][j]=1;
      }
    a.getinv();
    
    for (int i=1;i<=n;i++) if (!b[i]){
      for (int j=i+1;j<=n+addin;j++)
        if (edge[i][j]&&a.a[j][i]!=0&&!b[j]){
          fin[i]=j;fin[j]=i;
          b[i]=1;b[j]=1;
          a.elim(i,j);
          a.elim(j,i);
          break;
        }
    }
    for (int i=1;i<=n;i++) printf("%d ",(fin[i]<=n ? fin[i]:0));
  }

 -------------------------------

以上方法可以解决必选点等问题。若只需求最大匹配,可发现最大匹配的点为tutte矩阵中最大线性无关组所对应的点。求最大匹配即可。

在求逆和消元中进行一定的常数优化。

#include <cstdio>
#include <iostream>
#include <cmath>
#define LL long long
using namespace std;

  const LL mo=1e9+7;
  LL sed=12939512;

  struct linknode{
      int pre,next;
  }row[2001],col[2001];

  int n,m;
  int b[2001],fin[2001];
  int sid[124751][2],cnt,edge[2001][2001],lis[2001],inlis[2001];
  LL ran(){
      sed*=21409124;sed+=1249134;sed^=1234904;sed%=mo;
      return(sed);
  }

  LL qpow(LL bas,int powe){
      LL ret=1;
      for (;powe;bas*=bas,bas%=mo){
        if (powe&1) ret*=bas,ret%=mo;
      powe>>=1;    
    }
    return(ret);
  }

  struct matrix{
    LL a[1201][1201],tmp[1201][1201];
    int n,m;
    
    int getrank(){
      for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) tmp[i][j]=a[i][j];
      for (int i=1;i<=n;i++){
          for (int j=i;j<=n;j++) if (a[j][i]) {swap(a[j],a[i]);break;}
          if (!a[i][i]) continue;
          LL inv=qpow(a[i][i],mo-2);
        for (int j=i+1;j<=n;j++){
          LL bas=a[j][i]*inv%mo;
          if (!bas) continue;
          for (int k=i;k<=n;k++)
            if (a[i][k])
              a[j][k]-=a[i][k]*bas%mo,a[j][k]%=mo,a[j][k]+=mo,a[j][k]%=mo;
          }    
      }
      LL ret=0;
      for (int i=1;i<=n;i++) if (a[i][i]) ret++;
      return(ret);
    }
    
    void getinv(){
      for (int i=1;i<=n;i++) a[i][n+i]=1;
      for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) a[i][j]=(a[i][j]%mo+mo)%mo;
      for (int i=1;i<=n;i++){
        int po;LL maxi=0;
        for (int j=i;j<=n;j++){
          if (abs(a[j][i])>maxi){
            maxi=abs(a[j][i]);po=j;
          }
        }
        for (int j=1;j<=2*n;j++){
          LL t=a[i][j];a[i][j]=a[po][j];a[po][j]=t;
        }
        if (abs(maxi)==0) continue;
        
        LL rev=qpow(a[i][i],mo-2);    
        for (int j=i+1;j<=n;j++){
          if (!a[j][i]) continue;
          LL tim=a[j][i]*rev%mo;
          for (int k=i;k<=2*n;k++) if (a[i][k]) a[j][k]-=a[i][k]*tim%mo,a[j][k]%=mo;
          }
        }
        
        for (int i=1;i<=n;i++) for (int j=1;j<=2*n;j++) a[i][j]=(a[i][j]%mo+mo)%mo;
        for (int i=n;i>=1;i--){
          for (int j=i+1;j<=n;j++) if (a[i][j]){
            for (int k=n+1;k<=2*n;k++) if (a[j][k])
              a[i][k]-=a[i][j]*a[j][k]%mo,a[i][k]%=mo;
            a[i][j]=0;          
          }
          LL rev=qpow(a[i][i],mo-2);
          for (int j=n+1;j<=2*n;j++)
            if (a[i][j])
              a[i][j]*=rev,a[i][j]%=mo;
          a[i][i]=1;  
        }
        
        for (int i=1;i<=n;i++)
          for (int j=1;j<=n;j++)
            a[i][j]=(a[i][j+n]%mo+mo)%mo;
    }
    
    void elim(int x,int y){
      for (int i=0;i<=n;i=row[i].next)
        if (!b[i]){
          LL bas=a[i][y]*qpow(a[x][y],mo-2)%mo;
          for (int j=col[0].next;j<=n;j=col[j].next){
            a[i][j]-=a[x][j]*bas%mo;
            a[i][j]%=mo;a[i][j]+=mo;a[i][j]%=mo;
          }
        }
    }
  }a;

  void del(int po){
      b[po]=1;
      row[row[po].pre].next=row[po].next;
      row[row[po].next].pre=row[po].pre;
      col[col[po].pre].next=col[po].next;
      col[col[po].next].pre=col[po].pre;
  }

  int main(){      
      scanf("%d%d",&n,&m);
      a.n=a.m=n;
      for (int i=1;i<=m;i++){
        int t1,t2;
        scanf("%d%d",&t1,&t2);
        sid[++cnt][0]=t1;sid[cnt][1]=t2;
      LL num=ran();
      a.a[min(t1,t2)][max(t1,t2)]=num;
      a.a[max(t1,t2)][min(t1,t2)]=mo-num;
    }
    int ans=a.getrank()/2;
    printf("%d\n",ans);
    
    m=0;
    for (int i=1;i<=n;i++) if (a.a[i][i]) lis[++m]=i,inlis[i]=m;
    for (int i=1;i<=m;i++){
      row[i].pre=i-1;row[i].next=i+1;
      col[i].pre=i-1;col[i].next=i+1;
    }
    row[0].next=col[0].next=1;
    for (int i=1;i<=a.n;i++) for (int j=1;j<=a.m;j++) a.a[i][j]=0;
    a.n=m;a.m=m;
    for (int i=1;i<=cnt;i++) if (inlis[sid[i][0]]&&inlis[sid[i][1]]){
      LL num=ran(),u=inlis[sid[i][0]],v=inlis[sid[i][1]];
      if (u>v) swap(u,v);
      a.a[u][v]=num;
      a.a[v][u]=-num;
      edge[u][v]=1;edge[v][u]=1;
    }
    a.getinv();
    
    for (int i=1;i<=m;i++) if (!b[i]){
      for (int j=i+1;j<=m;j++)
        if (edge[i][j]&&a.a[j][i]!=0&&!b[j]){
          fin[lis[i]]=lis[j];fin[lis[j]]=lis[i];
          del(i);del(j);
          a.elim(i,j);
          a.elim(j,i);
          break;
        }
    }
    for (int i=1;i<=n;i++) printf("%d ",fin[i]);
  }

 

转载于:https://www.cnblogs.com/zhujiangning/p/7157465.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值