知识点 - 高斯消元 线性方程组 SLAE

知识点 - 高斯消元 线性方程组 SLAE

a 11 x 1 + a 12 x 2 + ⋯ + a 1 m x m = b 1 a_{11} x_1 + a_{12} x_2 + \dots + a_{1m} x_m = b_1 a11x1+a12x2++a1mxm=b1
a 21 x 1 + a 22 x 2 + ⋯ + a 2 m x m = b 2 a_{21} x_1 + a_{22} x_2 + \dots + a_{2m} x_m = b_2 a21x1+a22x2++a2mxm=b2
… \dots
a n 1 x 1 + a n 2 x 2 + ⋯ + a n m x m = b n a_{n1} x_1 + a_{n2} x_2 + \dots + a_{nm} x_m = b_n an1x1+an2x2++anmxm=bn

矩阵形式即

A x = b Ax = b Ax=b,

模质数下的,求逆元即可。

a 11 x 1 + a 12 x 2 + ⋯ + a 1 m x m ≡ b 1 ( m o d p ) a_{11} x_1 + a_{12} x_2 + \dots + a_{1m} x_m \equiv b_1 \pmod p a11x1+a12x2++a1mxmb1(modp)
a 21 x 1 + a 22 x 2 + ⋯ + a 2 m x m ≡ b 2 ( m o d p ) a_{21} x_1 + a_{22} x_2 + \dots + a_{2m} x_m \equiv b_2 \pmod p a21x1+a22x2++a2mxmb2(modp)
… \dots
a n 1 x 1 + a n 2 x 2 + ⋯ + a n m x m ≡ b n ( m o d p ) a_{n1} x_1 + a_{n2} x_2 + \dots + a_{nm} x_m \equiv b_n \pmod p an1x1+an2x2++anmxmbn(modp)

解决问题类型:

模板及复杂度

/*高斯消元,系数矩阵为a[i][j],i=1…n,j=1…n,常数为a[i][n+1],i=1…n,返回值为是否有唯一解,答案存在a[i][n+1]*/
bool gauss(int n) {
    for (int i = 1; i <= n; i++) {
        int k = i;
        for (int j = i + 1; j <= n; j++)if (fabs(a[j][i]) > fabs(a[k][i]))k = j;
        if (fabs(del = a[k][i]) < eps)return 0;
        for (int j = i; j <= n + 1; j++)swap(a[i][j], a[k][j]);
        for (int j = i; j <= n + 1; j++)a[i][j] /= del;
        for (k = 1; k <= n; k++)if (k != i) {
            del = a[k][i];
            for (int j = i; j <= n + 1; j++)a[k][j] -= a[i][j] * del;
        }
    }
    return 1;
}

下面这个模板未知数和方程数可以不同。读入 A , b A,b A,b 矩阵,返回解的个数 ( 0 , 1 , or  ∞ ) (0, 1,\textrm{or } \infty) (0,1,or ). O ( n 3 ) O(n^3) O(n3).

/*
col row 为当前的行,列
where[i]存x_i的第几行不是0,如果为-1则说明这个变量是独立的(可以任意取)
最后把独立变量赋为0,看方程有没有解
*/
int gauss (vector < vector<double> > a, vector<double> & ans) {
	int n = (int) a.size();
	int m = (int) a[0].size() - 1;

	vector<int> where (m, -1);
	for (int col=0, row=0; col<m && row<n; ++col) {
		int sel = row;
		for (int i=row; i<n; ++i)
			if (abs (a[i][col]) > abs (a[sel][col]))
				sel = i;
		if (abs (a[sel][col]) < EPS)
			continue;
		for (int i=col; i<=m; ++i)
			swap (a[sel][i], a[row][i]);
		where[col] = row;

		for (int i=0; i<n; ++i)
			if (i != row) {
				double c = a[i][col] / a[row][col];
				for (int j=col; j<=m; ++j)
					a[i][j] -= a[row][j] * c;
			}
		++row;
	}

	ans.assign (m, 0);
	for (int i=0; i<m; ++i)
		if (where[i] != -1)
			ans[i] = a[where[i]][m] / a[where[i]][i];
	for (int i=0; i<n; ++i) {
		double sum = 0;
		for (int j=0; j<m; ++j)
			sum += ans[j] * a[i][j];
		if (abs (sum - a[i][m]) > EPS)
			return 0;
	}

	for (int i=0; i<m; ++i)
		if (where[i] == -1)
			return INF;
	return 1;
}

模2下的模板,先削成上三角,然后回带 O ( n m ) O(nm) O(nm)

int gauss (vector < bitset<N> > a, int n, int m, bitset<N> & ans) {
	vector<int> where (m, -1);
	for (int col=0, row=0; col<m && row<n; ++col) {
		for (int i=row; i<n; ++i)
			if (a[i][col]) {
				swap (a[i], a[row]);
				break;
			}
		if (! a[row][col])
			continue;
		where[col] = row;

		for (int i=0; i<n; ++i)
			if (i != row && a[i][col])
				a[i] ^= a[row];
		++row;
	}
        // The rest of implementation is the same as above
}

例题

Gambler Bo

代码

//https://www.cnblogs.com/forever97/p/hdu5755.html
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
#define rep(i,n) for(int i=1;i<=n;i++)
const int N=35;
int ans,n,m,T,a[N][N],p[N][N],f[N][N][N];
int DP(int i,int j,int k){
    int t=(f[i][j-1][k]+2*f[i][j][k]+f[i][j+1][k]+f[i-1][j][k])%3;
    return ((3-t)%3+3)%3;
}
int GetAns(int i,int j){
    int t=(p[i][j-1]+2*p[i][j]+p[i][j+1]+p[i-1][j]+a[i][j])%3;
    return ((3-t)%3+3)%3;
}
void Gauss(int n,int m) { 
    int d,i,j,k,h,w=0; 
    for(i=1,j=1;j<m;j++,w=0){ 
        for(k=i;k<=n;k++)if(p[k][j])w=k; 
        if(w){ 
            for(k=j;k<=m;k++)swap(p[i][k],p[w][k]); 
            for(k=i+1;k<=n;k++) 
            if(p[k][j]){ 
                d=p[k][j]*p[i][j]%3;
                for(h=j;h<=m;h++)p[k][h]=(p[k][h]-d*p[i][h]+6)%3; 
            }i++; 
        }if(i>n)break; 
    }for(j=1;j<=m;j++)f[1][j][j]=0; 
    for(j=i-1;j;j--){ 
        for(k=1;k<m;k++)if(p[j][k])break; 
        for (d=0,h=k+1;h<m;h++)if(f[1][h][h]&&p[j][h])d=(d+f[1][h][h]*p[j][h])%3; 
        f[1][k][k]=p[j][k]*(3-d+p[j][m])%3; 
    }memset(p,0,sizeof(p)); 
    for(j=1;j<=m;j++)p[1][j]=(f[1][j][j]+3)%3; 
} 
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);
        rep(i,n)rep(j,m)scanf("%d",&a[i][j]);
        memset(f,0,sizeof(f));
        rep(i,m)f[1][i][i]=1;
        rep(i,n)rep(j,m){
            f[i+1][j][m+1]=(3-a[i][j]+DP(i,j,m+1))%3;
            rep(k,m)f[i+1][j][k]=DP(i,j,k);
        }memset(p,0,sizeof(p));
        rep(i,m){
            p[i][m+1]=((3-f[n+1][i][m+1])%3+3)%3;
            rep(j,m)p[i][j]=f[n+1][i][j];
        }Gauss(m,m+1);ans=0;
        rep(i,m)ans+=p[1][i];
        for(int i=2;i<=n;i++)rep(j,m)p[i][j]=GetAns(i-1,j),ans+=p[i][j];
        printf("%d\n",ans);
        rep(i,n)rep(j,m)rep(k,p[i][j])printf("%d %d\n",i,j);
    }return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Best KeyBoard

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值