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

题解

Ca Fe Zn Se

显然我们既然初始矩阵就能通过线性变换变成单位矩阵,则该矩阵一定有逆

没有逆输出NIE

而且因为这些向量两两正交,则表示一个向量的时候表示方法唯一

那么我们求一个逆可以求出这个矩阵消成单位矩阵的线性表示,再拿第二个矩阵和逆矩阵相乘可以得到第二个矩阵每个行向量用第一个矩阵的行向量唯一的表示方法

如果第二套的第k个行向量的表示里第一行h个行向量系数不为0,则h可以被k替代

建图二分图匹配,先求一个匹配出来,然后对于每个点从前往后固定匹配看看能不能使得靠前的更小

说的很高端吧

算了我简单一点说

就是一考虑初始的矩阵,什么两三行加加减减乘个系数,是可以消成单位1的,这个可以用类似高斯消元的方法解决,实际上如果你了解矩阵求逆的话你就知道我在给这个矩阵求逆矩阵
如果消不成就是NIE了

如果你不了解的话,你可以把每次变换每个行所用到的系数记下来,变成另一个矩阵\(B\)

那么我们考虑第二套的某个向量,
\((b_{1},b_{2}...b_{n}) = \sum_{i = 1}^{n} c_{i} (a_{1},a_{2}..a_{n})\)
显然如果\(c_{i}\)有数的话,我门可以把\(c_{i}\)移到等号左边,把第二套的这个向量移到等式右边,就证明\(i\)可以被这个向量换掉了
这个系数可以用两个矩阵相乘求出来

图建出来了,那就是跑二分图了

我们先求出一个完备匹配来,没有就是NIE,有的话对于\(1-n\)从小到大枚举能更新的匹配点,然后把这个点强制不选再跑二分图看看会不会合法

代码

#include <bits/stdc++.h>
#define fi first
#define se second
#define pii pair<int,int>
#define pdi pair<db,int>
#define mp make_pair
#define pb push_back
#define enter putchar('\n')
#define space putchar(' ')
#define eps 1e-8
#define mo 974711
#define MAXN 305
//#define ivorysi
using namespace std;
typedef long long int64;
typedef double db;
template<class T>
void read(T &res) {
    res = 0;char c = getchar();T f = 1;
    while(c < '0' || c > '9') {
    if(c == '-') f = -1;
    c = getchar();
    }
    while(c >= '0' && c <= '9') {
    res = res * 10 + c - '0';
    c = getchar();
    }
    res *= f;
}
template<class T>
void out(T x) {
    if(x < 0) {x = -x;putchar('-');}
    if(x >= 10) {
    out(x / 10);
    }
    putchar('0' + x % 10);
}
int N;
int g[305][305],matc[305],matk[305];
bool vis[305];
bool flag = 0;
struct Matrix {
    db f[305][305];
    Matrix() {memset(f,0,sizeof(f));}
    void unit() {
    for(int i = 1 ; i <= N ; ++i) {
        f[i][i] = 1.0;
    }
    }
    friend Matrix operator * (const Matrix &a,const Matrix &b) {
    Matrix c;
    for(int i = 1 ; i <= N ; ++i) {
        for(int j = 1 ; j <= N ; ++j) {
        for(int k = 1 ; k <= N ; ++k) {
            c.f[i][j] += a.f[i][k] * b.f[k][j];
        }
        }
    }
    return c;
    }
    friend Matrix operator ~(Matrix a) {
    Matrix b;
    b.unit();
    for(int i = 1 ; i <= N ; ++i) {
        int l = i;
        for(int j = i + 1; j <= N ; ++j) {
        if(fabs(a.f[j][i]) > fabs(a.f[l][i])) l = j;
        }
        if(fabs(a.f[l][i]) < 1e-8) {flag = 1;return b;}
        if(i != l) {
        for(int j = 1 ; j <= N ; ++j) {
            swap(a.f[i][j],a.f[l][j]);
            swap(b.f[i][j],b.f[l][j]);
        }
        }
        db t = 1.0 / a.f[i][i];
        for(int j = 1 ; j <= N ; ++j) {
        a.f[i][j] *= t;
        b.f[i][j] *= t;
        }
        
        for(int j = 1 ; j <= N ; ++j) {
        if(i == j) continue;
        db t = a.f[j][i];
        for(int k = 1 ; k <= N ; ++k) {
            a.f[j][k] -= t * a.f[i][k];
            b.f[j][k] -= t * b.f[i][k];
        }
        }
    }
    return b;
    }
}A,B,C;

bool match(int u) {
    for(int i = 1 ; i <= N ; ++i) {
    if(g[u][i]) {
        if(!vis[i]) {
        vis[i] = 1;
        if(!matc[i] || match(matc[i])) {
            matc[i] = u;matk[u] = i;
            return true;
        }
        }
    }
    }
    return false;
}
void Solve() {
    read(N);
    for(int i = 1 ; i <= N ; ++i) {
    for(int j = 1 ; j <= N ; ++j) {
        scanf("%lf",&A.f[i][j]);
    }
    }
    for(int i = 1 ; i <= N ; ++i) {
    for(int j = 1 ; j <= N ; ++j) {
        scanf("%lf",&C.f[i][j]);
    }
    }
    B = ~A;
    if(flag) {
    puts("NIE");return;
    }
    C = C * B;
    for(int i = 1 ; i <= N ; ++i) {
    for(int j = 1 ; j <= N ; ++j) {
        if(fabs(C.f[i][j]) > 1e-8) {
        g[j][i] = 1;
        }
    }
    }
    for(int i = 1 ; i <= N ; ++i) {
    memset(vis,0,sizeof(vis));
    if(!match(i)) {
        puts("NIE");return;
    }
    }
    puts("TAK");
    for(int i = 1 ; i <= N ; ++i) {
    for(int j = 1 ; j <= N ; ++j) {
        if(!g[i][j]) continue;
        if(matk[i] == j) break;
        if(matc[j] < i) continue;
        memset(vis,0,sizeof(vis));
        for(int k = 1 ; k < i ; ++k) vis[matk[k]] = 1;
        vis[j] = 1;
        int t = matk[i];
        matc[t] = 0;
        if(match(matc[j])) {
        matk[i] = j;matc[j] = i;
        break;
        }
        else {
        matc[t] = i;
        }
    }
    }
    for(int i = 1 ; i <= N ; ++i) {
    out(matk[i]);enter;
    }
}
int main() {
#ifdef ivorysi
    freopen("f1.in","r",stdin);
#endif
    Solve();
    return 0;
}

转载于:https://www.cnblogs.com/ivorysi/p/10094717.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值