高斯消元小结

Ⅰ. 高斯消元认知

  • $这东西英文名叫Gaussian Elimination$
  • $高斯消元对矩阵进行操作,对满足一定运算规律的方程利用矩阵初等变换进行消元$
  • $主要用来求解线性方程组、矩阵的秩、以及可逆方阵的逆矩阵$
  • $还可以用来线性空间的基向量,比如线性基(线性无关的极大子集)$
  • $PS:关于线性相关,具体可以读2014集训队论文《浅谈线性相关》$

Ⅱ. 高斯消元模版

浮点数高斯消元

double a[N][N], ans[N];
bool isFreeX[N];

void getAns(int n, int m, int r) {
//    memset(ans, 0, sizeof ans);  //not necessary
    for(int i = r - 1; ~i; --i) {
        for(int j = 0; j < m; ++j) {
            if(!sgn(a[i][j])) continue;
            ans[j] = a[i][m];
            for(int k = j + 1; k < m; ++k)
                ans[j] -= a[i][k] * ans[k];
            ans[j] = ans[j] / a[i][j];
            break;
        }
    }
}

int gauss(int n, int m) {
    for(int i = 0; i < m; ++i) isFreeX[i] = false;
    int r = 0, c = 0;
    for(; r < n && c < m; ++r, ++c) {
        int maxR = r;       //row transform
        for(int i = r + 1; i < n; ++i)
            if(abs(a[i][c]) > abs(a[maxR][c])) maxR = i;
        if(maxR != r) swap(a[maxR], a[r]);

        if(!sgn(a[r][c])) { --r; isFreeX[c] = true; continue;}

        //eliminate coefficient
        for(int i = r + 1; i < n; ++i) {
            if(sgn(a[i][c])) {
                double delta = a[i][c] / a[r][c];
                for(int j = c; j <= m; ++j)
                    a[i][j] -= delta * a[r][j];
            }
        }
    }
    for(int i = r; i < n; i++) if(sgn(a[i][m])) return -1;

    getAns(n, m, r);

    //at last, r is rank, m - r is the number of freeX
    return r;
}

模意义下高斯消元

int a[N][N], ans[N];
bool isFreeX[N];

inline int inv(int x) {return 1;}

int getAns(int n, int m, int r) {
    int ret = 0;
    for(int i = r - 1; ~i; --i) {
        for(int j = 0; j < m; ++j) {
            if(!a[i][j]) continue;
            ans[j] = a[i][m];
            for(int k = j + 1; k < m; ++k) {
                ans[j] -= a[i][k] * ans[k];
                ans[j] %= MOD;
                if(ans[j] < 0) ans[j] += MOD;
            }
            ans[j] = ans[j] * inv(a[i][j]) % MOD;
            break;
        }
    }
    for(int i = 0; i < m; ++i) ret += ans[i];
    return ret;
}

int gauss(int n, int m) {
    for(int i = 0; i < m; ++i) isFreeX[i] = false;
    int r = 0, c = 0;
    for(; r < n && c < m; ++r, ++c) {
        int maxR = r;       //row transform
        for(int i = r + 1; i < n; ++i)
            if(abs(a[i][c]) > abs(a[maxR][c])) maxR = i;
        if(maxR != r) swap(a[maxR], a[r]);

        if(!a[r][c]) { --r; isFreeX[c] = true; continue;}

        //eliminate coefficient
        for(int i = r + 1; i < n; ++i) {
            if(a[i][c]) {
                int delta = a[i][c] * inv(a[r][c]);
                for(int j = c; j <= m; ++j) {
                    a[i][j] -= delta * a[r][j];
                    a[i][j] %= MOD;
                    if(a[i][j] < 0) a[i][j] += MOD;
                }
            }
        }
    }
    for(int i = r; i < n; i++) if(a[i][m]) return -1;

    //at last, r is rank, m - r is the number of freeX
    return r;
}

int getMinAns(int n, int m, int r) {
    int ret = INF, freeX = m - r;
    for(int s = 0; s < 1 << freeX; ++s) {
        if(__builtin_popcount(s) >= ret) continue;
        int cnt = 0;
        for(int j = 0; j < m; ++j) {
            if(isFreeX[j]) {
                ans[j] = s >> cnt & 1;
                ++cnt;
            }
        }
        ret = min(ret, getAns(n, m, r));
    }
    return ret;
}

XOR高斯消元

int a[N][N], ans[N];

int xorGauss() {
    int r = 0, c = 0;
    for(; r < n && c < n; ++r, ++c) {
        int p = r;
        for(; p < n; ++p) if(a[p][c]) break;
        if(p == n) {--r; continue;}
        swap(a[p], a[r]);

        for(int i = 0; i < n; ++i) {
            if(i != r && a[i][c]) {
                for(int j = c; j <= n; ++j)
                    a[i][j] ^= a[r][j];
            }
        }
    }
    for(int i = r; i < n; ++i) if(a[i][n]) return -1;

    //memset(ans, 0, sizeof ans);  //not necessary
    for(int i = r - 1; ~i; --i)
        for(int j = 0; j < n; ++j)
            if(a[i][j]) {ans[j] = a[i][n]; break;}
    return r;
}

Ⅲ. 题目讲解

HDU 4200 Bad Wiring

分析:
$开关问题,POJ的几个数据都比较弱,所以就选了这个$
$建立模2意义下的方程组,然后高斯消元,要求最小解,二进制枚举自由变元回代即可$
代码:

//
//  Created by TaoSama on 2016-07-28
//  Copyright (c) 2016 TaoSama. All rights reserved.
//
#pragma comment(linker, "/STACK:102400000,102400000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>

using namespace std;
#define pr(x) cout << #x << " = " << x << "  "
#define prln(x) cout << #x << " = " << x << endl
const int N = 100 + 10, INF = 0x3f3f3f3f, MOD = 2;

int n, d;
int a[N][N], ans[N];
bool isFreeX[N];

inline int inv(int x) {return 1;}

int getAns(int n, int m, int r) {
    int ret = 0;
    for(int i = r - 1; ~i; --i) {
        for(int j = 0; j < m; ++j) {
            if(!a[i][j]) continue;
            ans[j] = a[i][m];
            for(int k = j + 1; k < m; ++k) {
                ans[j] -= a[i][k] * ans[k];
                ans[j] %= MOD;
                if(ans[j] < 0) ans[j] += MOD;
            }
            ans[j] = ans[j] * inv(a[i][j]) % MOD;
            break;
        }
    }
    for(int i = 0; i < m; ++i) ret += ans[i];
    return ret;
}

int gauss(int n, int m) {
    for(int i = 0; i < m; ++i) isFreeX[i] = false;
    int r = 0, c = 0;
    for(; r < n && c < m; ++r, ++c) {
        int maxR = r;       //row transform
        for(int i = r + 1; i < n; ++i)
            if(abs(a[i][c]) > abs(a[maxR][c])) maxR = i;
        if(maxR != r) swap(a[maxR], a[r]);

        if(!a[r][c]) { --r; isFreeX[c] = true; continue;}

        //eliminate coefficient
        for(int i = r + 1; i < n; ++i) {
            if(a[i][c]) {
                int delta = a[i][c] * inv(a[r][c]);
                for(int j = c; j <= m; ++j) {
                    a[i][j] -= delta * a[r][j];
                    a[i][j] %= MOD;
                    if(a[i][j] < 0) a[i][j] += MOD;
                }
            }
        }
    }
    for(int i = r; i < n; i++) if(a[i][m]) return -1;

    //at last, r is rank, m - r is the number of freeX
    return r;
}

int getMinAns(int n, int m, int r) {
    int ret = INF, freeX = m - r;
    for(int s = 0; s < 1 << freeX; ++s) {
        if(__builtin_popcount(s) >= ret) continue;
        int cnt = 0;
        for(int j = 0; j < m; ++j) {
            if(isFreeX[j]) {
                ans[j] = s >> cnt & 1;
                ++cnt;
            }
        }
        ret = min(ret, getAns(n, m, r));
    }
    return ret;
}

int main() {
#ifdef LOCAL
    freopen("C:\\Users\\TaoSama\\Desktop\\in.txt", "r", stdin);
//  freopen("C:\\Users\\TaoSama\\Desktop\\out.txt","w",stdout);
#endif
    ios_base::sync_with_stdio(0);
    clock_t _ = clock();

    int t; scanf("%d", &t);
    while(t--) {
        scanf("%d%d", &n, &d);
        memset(a, 0, sizeof a);
        for(int i = 0; i < n; ++i) {
            int v; scanf("%d", &v);
            a[i][n] = -v + MOD;
            for(int j = max(0, i - d); j <= min(n - 1, i + d); ++j)
                a[i][j] = 1;
        }
        int r = gauss(n, n);
        if(~r) printf("%d\n", getMinAns(n, n, r));
        else puts("impossible");
    }

#ifdef LOCAL
    printf("\nTime cost: %.2fs\n", 1.0 * (clock() - _) / CLOCKS_PER_SEC);
#endif
    return 0;
}

SGU 275 To xor or not to xor

分析:
$求N个数的子集的最大异或和,高斯消元后求出的是极大线性无关组即线性基$
$线性基异或起来显然就是最大异或和了$
代码:

//
//  Created by TaoSama on 2016-07-28
//  Copyright (c) 2016 TaoSama. All rights reserved.
//
#pragma comment(linker, "/STACK:102400000,102400000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>

using namespace std;
#define pr(x) cout << #x << " = " << x << "  "
#define prln(x) cout << #x << " = " << x << endl
const int N = 1e5 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;

typedef long long LL;
int n;
LL a[N];

int xorGauss() {
    int r = 0, c = 63;
    for(; r < n && ~c; ++r, --c) {
        int p = r;
        for(; p < n; ++p) if(a[p] >> c & 1) break;
        if(p == n) {--r; continue;}
        swap(a[p], a[r]);

        for(int i = 0; i < n; ++i)
            if(i != r && a[i] >> c & 1) a[i] ^= a[r];
    }
    return r;
}

int main() {
#ifdef LOCAL
    freopen("C:\\Users\\TaoSama\\Desktop\\in.txt", "r", stdin);
//  freopen("C:\\Users\\TaoSama\\Desktop\\out.txt","w",stdout);
#endif
    ios_base::sync_with_stdio(0);
    clock_t _ = clock();

    scanf("%d", &n);
    for(int i = 0; i < n; ++i) scanf("%lld", a + i);

    int r = xorGauss();
    LL ans = 0;
    for(int i = 0; i < r; ++i) ans ^= a[i];
    printf("%lld\n", ans);


#ifdef LOCAL
    printf("\nTime cost: %.2fs\n", 1.0 * (clock() - _) / CLOCKS_PER_SEC);
#endif
    return 0;
}

HDU 2262 Where is the canteen

分析:
$高斯消元求解期望dp$
$f[x][y]:=(x, y)到终点的期望次数,显然终点的期望是0,即f[ex][ey]=0$
$期望倒推,f[x][y]=(\sum p_i\times f[nx][ny]) + 1$
$bfs求出每个点的可达性,之后对于每个点建立期望方程,然后高斯消元求解即可$
代码:

//
//  Created by TaoSama on 2016-07-28
//  Copyright (c) 2016 TaoSama. All rights reserved.
//
#pragma comment(linker, "/STACK:102400000,102400000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>

using namespace std;
#define pr(x) cout << #x << " = " << x << "  "
#define prln(x) cout << #x << " = " << x << endl
const int N = 15 * 15 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;
const double EPS = 1e-8;

int sgn(double x) {
    return x < -EPS ? -1 : x > EPS;
}

int n, m;
char s[N][N];
double a[N][N], ans[N];
bool isFreeX[N];

void getAns(int n, int m, int r) {
//    memset(ans, 0, sizeof ans);  //not necessary
    for(int i = r - 1; ~i; --i) {
        for(int j = 0; j < m; ++j) {
            if(!sgn(a[i][j])) continue;
            ans[j] = a[i][m];
            for(int k = j + 1; k < m; ++k)
                ans[j] -= a[i][k] * ans[k];
            ans[j] = ans[j] / a[i][j];
            break;
        }
    }
}

int gauss(int n, int m) {
    for(int i = 0; i < m; ++i) isFreeX[i] = false;
    int r = 0, c = 0;
    for(; r < n && c < m; ++r, ++c) {
        int maxR = r;       //row transform
        for(int i = r + 1; i < n; ++i)
            if(abs(a[i][c]) > abs(a[maxR][c])) maxR = i;
        if(maxR != r) swap(a[maxR], a[r]);

        if(!sgn(a[r][c])) { --r; isFreeX[c] = true; continue;}

        //eliminate coefficient
        for(int i = r + 1; i < n; ++i) {
            if(sgn(a[i][c])) {
                double delta = a[i][c] / a[r][c];
                for(int j = c; j <= m; ++j)
                    a[i][j] -= delta * a[r][j];
            }
        }
    }
    for(int i = r; i < n; i++) if(sgn(a[i][m])) return -1;

    getAns(n, m, r);

    //at last, r is rank, m - r is the number of freeX
    return r;
}

inline int ID(int x, int y) {
    return x * m + y;
}

bool can[N][N];
const int d[][2] = { -1, 0, 0, -1, 1, 0, 0, 1};
bool bfs(int sx, int sy) {
    memset(can, 0, sizeof can);
    queue<pair<int, int> > q;
    q.push({sx, sy});
    can[sx][sy] = true;

    bool ok = false;
    while(q.size()) {
        auto u = q.front(); q.pop();
        int x, y; tie(x, y) = u;
        if(s[x][y] == '$') ok = true;
        for(int i = 0; i < 4; ++i) {
            int nx = x + d[i][0], ny = y + d[i][1];
            if(nx < 0 || nx >= n || ny < 0 || ny >= m) continue;
            if(s[nx][ny] == '#' || can[nx][ny]) continue;
            can[nx][ny] = true;
            q.push({nx, ny});
        }
    }
    return ok;
}

int main() {
#ifdef LOCAL
    freopen("C:\\Users\\TaoSama\\Desktop\\in.txt", "r", stdin);
//  freopen("C:\\Users\\TaoSama\\Desktop\\out.txt","w",stdout);
#endif
    ios_base::sync_with_stdio(0);
    clock_t _ = clock();

    while(scanf("%d%d", &n, &m) == 2) {
        int sx, sy; sx = sy = -1;
        for(int i = 0; i < n; ++i) {
            scanf("%s", s[i]);
            if(~sx) continue;
            for(int j = 0; j < m; ++j)
                if(s[i][j] == '@') sx = i, sy = j;
        }
        if(!bfs(sx, sy)) {puts("-1"); continue;}

        //E_i = sum(E_j)/cnt + 1 => sum(E_j) - cnt*E_i = -cnt
        memset(a, 0, sizeof a);
        for(int i = 0; i < n; ++i) {
            for(int j = 0; j < m; ++j) {
                if(s[i][j] == '#') continue; //obstacle
                if(s[i][j] == '$') { //destination
                    a[ID(i, j)][ID(i, j)] = 1;
                    a[ID(i, j)][n * m] = 0; //E_exit = 0;
                    continue;
                }

                int cnt = 0;
                for(int k = 0; k < 4; ++k) {
                    int x = i + d[k][0], y = j + d[k][1];
                    if(x < 0 || x >= n || y < 0 || y >= m || !can[x][y]) continue;
                    ++cnt;
                    a[ID(i, j)][ID(x, y)] = 1;
                }
                a[ID(i, j)][ID(i, j)] = -cnt;
                a[ID(i, j)][n * m] = -cnt;
            }
        }

//        for(int i = 0; i < n * m; ++i)
//          for(int j = 0; j <= n * m; ++j)
//              printf("%.3f%c", a[i][j], " \n"[j == n * m]);

        gauss(n * m, n * m);
        double result = ans[ID(sx, sy)];
        printf("%.6f\n", result);
    }

#ifdef LOCAL
    printf("\nTime cost: %.2fs\n", 1.0 * (clock() - _) / CLOCKS_PER_SEC);
#endif
    return 0;
}