BZOJ 3168: [Heoi2013]钙铁锌硒维生素 [线性基 Hungary 矩阵求逆]

本文介绍了解决Heoi2013钙铁锌硒维生素问题的方法,通过构造二分图匹配模型,利用高斯约当消元求解矩阵逆,并找到字典序最小的完美匹配。

3168: [Heoi2013]钙铁锌硒维生素

题意:给一个线性无关组A,再给一个B,要为A中每个向量在B中选一个可以代替的向量,替换后仍然线性无关。判断可行和求字典序最小的解


PoPoQQQ orz

显然是一个二分图匹配的模型

A是一个线性基,用它把B中每个向量表示出来,那么\(B_i\)可以替换\(A_j\)当且仅当表示\(B_i\)用到了\(A_j\)

可是A并不是每一位独立,怎么求表示啊?

A和B可以看成两个矩阵(横向量组)
\(C*A=B \rightarrow C=B*A^{-1}\)
\(C_{i,j}=1\)说明表示\(B_i\)用到了\(A_j\),那么\(C^T\)就是这个二分图的邻接矩阵了

求矩阵的逆

这里说一种方法,对A进行高斯约当消元,右面的常数列换成单位矩阵。校园后,左面变成了单位矩阵,右面就是\(A^{-1}\)

二分图匹配字典序最小的解

求任意一个完美匹配,然后从1到n贪心选择字典序最小的解,方法和hungary类似,但是要比较匹配点和当前点的字典序

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
typedef unsigned long long ll;
const int N=305, P=1e9+7;
inline int read() {
    char c=getchar(); int x=0, f=1;
    while(c<'0' || c>'9') {if(c=='-')f=-1; c=getchar();}
    while(c>='0' && c<='9') {x=x*10+c-'0'; c=getchar();}
    return x*f;
}

inline ll Pow(ll a, int b) {
    ll ans=1;
    for(; b; b>>=1, a=a*a%P)
        if(b&1) ans=ans*a%P;
    return ans;
}
inline void mod(int &x) {if(x<0) x+=P; else if(x>=P) x-=P;}

int n, g[N][N]; char s[N];
struct Matrix {
    int a[N][N];
    Matrix(){memset(a, 0, sizeof(a));}
    int* operator [](int x) {return a[x];}
    inline void im() {for(int i=1; i<=n; i++) a[i][i]=1;}
    void print() {for(int i=1; i<=n; i++) for(int j=1; j<=n; j++) printf("%d%c",a[i][j],j==n?'\n':' ');}
}a, b, c;

Matrix inverse(Matrix a) {
    Matrix c; c.im();
    for(int i=1; i<=n; i++) {
        int r;
        for(r=i; r<=n; r++) if(a[r][i]) break;
        // r != n+1
        if(r!=i) for(int j=1; j<=n; j++) 
            swap(a[i][j], a[r][j]), swap(c[i][j], c[r][j]);
        ll inv = Pow(a[i][i], P-2); 
        for(int j=1; j<=n; j++) 
            a[i][j] = a[i][j]*inv%P, c[i][j] = c[i][j]*inv%P;
        for(int k=1; k<=n; k++) if(k!=i) {
            ll t = a[k][i]%P;
            for(int j=1; j<=n; j++) 
                mod(a[k][j] -= a[i][j]*t%P), mod(c[k][j] -= c[i][j]*t%P);
        }
    }
    return c;
}
Matrix operator *(Matrix a, Matrix b) {
    Matrix c;
    for(int i=1; i<=n; i++)
        for(int k=1; k<=n; k++) if(a[i][k])
            for(int j=1; j<=n; j++) 
                mod(c[i][j] += (ll)a[i][k]*b[k][j]%P);
    return c;
}

int vis[N], le[N];
bool dfs(int u) {
    for(int v=1; v<=n; v++) 
        if(!vis[v] && g[u][v]) {
            vis[v]=1;
            if(!le[v] || dfs(le[v])) {
                le[v]=u;
                return true;
            }
        }
    return false;
}
bool dfs(int u, int now) {
    for(int v=1; v<=n; v++) 
        if(!vis[v] && g[u][v]) {
            vis[v]=1;
            if(le[v]==now || (le[v]>now && dfs(le[v], now))) {
                le[v]=u;
                return true;
            }
        }
    return false;
}
int main() {
    freopen("in","r",stdin);
    //freopen("ferrous.in","r",stdin);
    //freopen("ferrous.out","w",stdout);
    n=read();
    for(int i=1; i<=n; i++) 
        for(int j=1; j<=n; j++) a[i][j] = read();
    for(int i=1; i<=n; i++) 
        for(int j=1; j<=n; j++) b[i][j] = read();
    c = b * inverse(a); //puts("c");c.print();
    //Matrix t = a * inverse(a); puts("t"); t.print();
    for(int i=1; i<=n; i++) for(int j=1; j<=n; j++) if(c[i][j]) g[j][i]=1;
    for(int i=1; i<=n; i++) {
        memset(vis, 0, sizeof(vis));
        if(!dfs(i)) {puts("NIE"); return 0;}
    }
    puts("TAK");
    for(int i=1; i<=n; i++) {
        memset(vis, 0, sizeof(vis));
        dfs(i, i);
    }
    for(int i=1; i<=n; i++) for(int j=1; j<=n; j++) if(le[j]==i) printf("%d\n",j);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值