【模板】高斯消元

高斯消元

一:求行列式
1除法

double gauss(){//高斯消元求行列式 
	int max_row;
	for(int row = 1, col = 1; row <= n && col <= n; row++, col++){
		//找到当前列绝对值最大的行 
		max_row = row;
		for(int i = row + 1; i <= n; i++) if(fabs(a[i][col]) > fabs(a[max_row][col] + eps)) max_row = i;
		if(fabs(a[max_row][col] < eps)) {return 0;}//有一个0, 行列式为0 
		//交换绝对值最大的行/ 减小除法的精度损失 
		if(row != max_row){
			for(int j = 1; j <= n; j++) swap(a[row][j], a[max_row][j]);
		}
		//消去当前列(只消下面行的)
		for(int i = row + 1; i <= n; i++){
			double temp = a[i][col] / a[row][col];
			for(int j = col; j <= n; j++){
				a[i][j] -= temp * a[row][j];
			}
		} 
	} 
	double ans = 1;
	for(int i = 1; i <= n; i++) ans = ans * a[i][i];
	return ans;
}

2逆元
ICPC 2021济南 J

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 100 + 10;
const ll mod = 1e9 + 7;

ll a[N][N];
int n;
ll qpow(ll base, ll pow, ll mod){
    ll ans = 1;
    while(pow){
        if(pow & 1) ans = ans * base % mod;
        base = base * base % mod;
        pow >>= 1;
    }
    return ans;
}

ll gauss_det_inv(ll a[][N], int n, ll mod){//高斯消元求行列式(逆元) 
    int flag = 1;
    for(int row = 1, col = 1; row <= n && col <= n; row++, col++){
        int max_row = row;
        //排除0的情况 
        for(int i = row; i <= n; i++){
            if(a[i][col] != 0) {max_row = i; break;}
        }
        if(row != max_row){
            for(int j = 1; j <= n; j++) swap(a[row][j], a[max_row][j]);
            flag = -flag;//交换行列式两行, 行列式值取反 
        }
        //做除法
        ll inv = qpow(a[row][col], mod - 2, mod);
        for(int i = row + 1; i <= n; i++){
            ll temp = a[i][col] * inv % mod;
            for(int j = col; j <= n; j++){
                a[i][j] = ((a[i][j] - temp * a[row][j]) % mod + mod) % mod;
            }
        } 
    }
    ll ans = 1;
    for(int i = 1; i <= n; i++){
        ans = ans * a[i][i] % mod;
    }
    return (ans * flag % mod + mod) % mod;
}

char str[10010];
ll get_det(ll mod){
    ll ret = 0;
    int len = strlen(str + 1);
    for(int i = 1; i <= len; i++){
        ret = (ret * 10 + (str[i] - '0')) % mod;
    }
    return ret;
}
void solve(){
    scanf("%d", &n);
    scanf("%s", str + 1);
    ll det = get_det(mod);
    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= n; j++){
            scanf("%lld", &a[i][j]);
        }
    }
    ll ans = gauss_det_inv(a, n, mod);
    if(ans == det)printf("+\n");
    else printf("-\n");
}

int main() {
    int t = 1;
    scanf("%d", &t);
    while(t--)solve();
    
    return 0;
}

3解线性方程组
1.1: 求具体的解
(1)唯一解

void gauss(double mat[N][N], double extra[N]/*矩阵的增广部分*/, int rank/*矩阵的秩*/){//化为对角矩阵
	//数据保证唯一解
	int maxRow;
	for(int row = 1, col = 1; row <= rank && col <= rank; row++, col++){
		//找到当前列绝对值最大的行 
		maxRow = row;
		for(int i = row + 1; i <= rank; i++) if(fabs(mat[i][col]) > fabs(mat[maxRow][col])) maxRow = i;
		//交换绝对值最大的行/ 减小除法的精度损失 
		if(row != maxRow){
			for(int j = 1; j <= rank; j++) swap(mat[row][j], mat[maxRow][j]);
			swap(extra[row], extra[maxRow]);
		}
		for(int i = 1; i <= rank; i++){
			if(i == row) continue;
			double rate = mat[i][col] / mat[row][col];
			for(int j = col; j <= rank; j++){
				mat[i][j] -= rate * mat[row][j];
			}
			extra[i] -= rate * extra[row];
		}
	}
	//除掉系数
	for(int i = 1; i <= rank; i++){
		extra[i] /= mat[i][i];
	}
}

例题
ACwing207 球形空间产生器

#include <bits/stdc++.h>
using namespace std;
const int N = 15;
const double eps = 1e-8;
int n, m;
double a[N][N];
double mat[N][N], extra[N];//矩阵的增广部分
void gauss(double mat[N][N], double extra[N]/*矩阵的增广部分*/, int rank/*矩阵的秩*/){//化为对角矩阵
	//数据保证唯一解
	int maxRow;
	for(int row = 1, col = 1; row <= rank && col <= rank; row++, col++){
		//找到当前列绝对值最大的行 
		maxRow = row;
		for(int i = row + 1; i <= rank; i++) if(fabs(mat[i][col]) > fabs(mat[maxRow][col])) maxRow = i;
		//交换绝对值最大的行/ 减小除法的精度损失 
		if(row != maxRow){
			for(int j = 1; j <= rank; j++) swap(mat[row][j], mat[maxRow][j]);
			swap(extra[row], extra[maxRow]);
		}
		for(int i = 1; i <= rank; i++){
			if(i == row) continue;
			double rate = mat[i][col] / mat[row][col];
			for(int j = col; j <= rank; j++){
				mat[i][j] -= rate * mat[row][j];
			}
			extra[i] -= rate * extra[row];
		}
	}
	//除掉系数
	for(int i = 1; i <= rank; i++){
		extra[i] /= mat[i][i];
	}
}

void solve(){
	cin >> n;
	for(int i = 1; i <= n + 1; i++){
		for(int j = 1; j <= n; j++){
			cin >> a[i][j];
		}
	}
	for(int i = 1; i <= n; i++){
		for(int j = 1; j <= n; j++){
			mat[i][j] = 2 * (a[i + 1][j] - a[i][j]);
			extra[i] += (a[i + 1][j] * a[i + 1][j] - a[i][j] * a[i][j]);
		}
	}
	gauss(mat, extra, n);
	for(int i = 1; i <= n; i++){
		cout << fixed << setprecision(3) << extra[i]<< " ";
	}
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	int t = 1;
	while(t--) solve();

	return 0;
}


4异或方程组
1.1求解的个数

int gaussXor(int mat[N], int rank) {//化为对角矩阵
	//由于异或只有0和1,所以将一行的状态压缩为一个二进制数,故mat只有一维
	//将矩阵的增广部分压缩进mat的第0位
	//Q: 为什么不是最高位
	//A: 二进制数高位到低位权值递减, 只有当系数矩阵一行的所有数均为0时, 才考虑矩阵的增广部分是否为0

	//返回值: 解的个数
	int maxRow;
	for (int row = 1; row <= rank; row++) {
		//找到当前值最大的行 
		for (int i = row + 1; i <= rank; i++) {
			if (mat[i] > mat[row]) swap(mat[i], mat[row]);
		}
		//判断接下来的行存在系数全为0的行
		if (mat[row] == 0) { return 1 << (rank - row + 1); }
		if (mat[row] == 1) { return 0; }
		for (int bit = n; bit; bit--) {
			if(mat[row] >> bit & 1) {
				for (int i = 1; i <= rank; i++) {
					if(i == row) continue;
					if(mat[i] >> bit & 1) mat[i] ^= mat[row];
				}
				break;
			}
		}
	}
	return 1;
}

例题
ACwing208 开关问题

#include <bits/stdc++.h>
using namespace std;
const int N = 15;
int n, m;
int mat[N];//矩阵的增广部分
int gaussXor(int mat[N], int rank) {//化为对角矩阵
	//由于异或只有0和1,所以将一行的状态压缩为一个二进制数,故mat只有一维
	//将矩阵的增广部分压缩进mat的第0位
	//Q: 为什么不是最高位
	//A: 二进制数高位到低位权值递减, 只有当系数矩阵一行的所有数均为0时, 才考虑矩阵的增广部分是否为0

	//返回值: 解的个数
	int maxRow;
	for (int row = 1; row <= rank; row++) {
		//找到当前值最大的行 
		for (int i = row + 1; i <= rank; i++) {
			if (mat[i] > mat[row]) swap(mat[i], mat[row]);
		}
		//判断接下来的行存在系数全为0的行
		if (mat[row] == 0) { return 1 << (rank - row + 1); }
		if (mat[row] == 1) { return 0; }
		for (int bit = n; bit; bit--) {
			if(mat[row] >> bit & 1) {
				for (int i = 1; i <= rank; i++) {
					if(i == row) continue;
					if(mat[i] >> bit & 1) mat[i] ^= mat[row];
				}
				break;
			}
		}
	}
	return 1;
}

void solve(){
	cin >> n;
	for (int i = 1; i <= n; i++) {
		cin >> mat[i];
	}
	for (int i = 1; i <= n; i++) {
		int t;
		cin >> t;
		mat[i] ^= t;
		mat[i] |= 1 << i;
	}
	int x, y;
	while (cin >> x >> y && x && y) {
		mat[y] |= 1 << x;
	}
	int ans = gaussXor(mat, n);
	if(ans) cout << ans << endl;
	else cout << "Oh,it's impossible~!!" << endl;
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	int t = 1;
	cin >> t;
	while (t--) solve();

	return 0;
}


5 求一组基, 并用这组基进行一系列操作
ACwing210 异或运算

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 510;
bool canGetZero = false;
int numOfBase;//最多能取到多少数
void gaussXor (vector<ll>& mat, int n) {
	//此题中高斯消元的主要作用是 得到一组线性无关的基
	int maxRow;
	numOfBase = n;
	for (int row = 1; row <= n; row++) {
		maxRow = 0;
		for (int i = row; i <= n; i++) {
			if (!maxRow || mat[i] > mat[maxRow]) {
				maxRow = i;
			}
		}
		if (mat[maxRow] == 0) {canGetZero = true; numOfBase = row - 1; return;}
		swap(mat[row], mat[maxRow]);
		for (int bit = 60; bit >= 0; bit--) {
			if (mat[row] >> bit & 1) {
				for (int i = 1; i <= n; i++) {
					if (i == row) continue;
					if (mat[i] >> bit & 1) mat[i] ^= mat[row];
				}
				break;
			}
		}
	}
}
void solve(int cas) {
	canGetZero = false;
	cout << "Case #" << cas << ":" << endl;
	int n;
	cin >> n;
	vector<ll> mat(n + 1);
	for (int i = 1; i <= n; i++) {
		cin >> mat[i];
	}
	gaussXor(mat, n);
	int q;
	cin >> q;
	for (int i = 1; i <= q; i++) {
		ll k;
		cin >> k;
		k--;
		if (!canGetZero) k++; 
		ll ans = 0;
		if (k >= (1ll << numOfBase)) cout << -1 << endl;
		else {
			for (int i = 0; i < numOfBase; i++) {
				if (k >= (1ll << (numOfBase - 1 - i))) {
					k -= (1ll << (numOfBase - 1 - i));
					ans ^= mat[i + 1];
				}
			}
			cout << ans << endl;
		}
	}
}
int main() {
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	int t = 1;
	cin >> t;
	for(int i = 1; i <= t; i++) solve(i);

	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值