高斯消元习题

高斯消元

    • 概念
    • 浮点数模板
      • P3389 【模板】高斯消元法
    • 异或方程组
      • 模板
      • 开关问题 POJ - 1830
      • EXTENDED LIGHTS OUT

概念

  • 自由变元:取非零行的首非零元所在列对应的变元为约束变元,其余变元取作自由变元
    比如 x 1 + x 2 + x 3 = 3 x_1+x_2+x_3=3 x1+x2+x3=3 ,这个式子的自由变元为 2 ,不确定变元为 3。因为这个式子中,当两个变元确定之后,最后一个量也确定了。
  • 列主元消去法:取当前列 r o w row row 以下的最大值所在行与 r o w row row 所在行交换,这样可以将较大的数放在第row列,即除数的位置,从而减小误差

浮点数模板

P3389 【模板】高斯消元法

高斯消元习题_第1张图片链接:https://www.luogu.com.cn/problem/P3389

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=110;

int n;
double a[maxn][maxn];
double x[maxn];

int guess(double a[][maxn],int n,int m)
{
	for(int row=1,col=1;row<=n&&col<=m;++row,++col)
	{
		int maxrow=row;
		for(int i=row+1;i<=n;++i)
			if(abs(a[i][col])>abs(a[row][col])) maxrow=i;
		if(maxrow!=row)
		{
			for(int i=col;i<=m+1;++i)
				swap(a[row][i],a[maxrow][i]);
		}
		if(a[row][col]==0) return -1;//存在自由变元
		for(int i=row+1;i<=n;++i)
		{
			double tmp=a[i][col]/a[row][col];
			for(int j=col;j<=m+1;++j)
				a[i][j]-=a[row][j]*tmp;
		}
	} 
	for(int i=n;i>=1;--i)
	{
		double res=a[i][m+1];
		for(int j=i+1;j<=m;++j)
			res-=a[i][j]*x[j];
		x[i]=res/a[i][i];
	}
	return 0;
}

int main()
{	
	scanf("%d",&n);
	for(int i=1;i<=n;++i)
		for(int j=1;j<=n+1;++j)
			scanf("%lf",&a[i][j]);
	int res=guess(a,n,n);
	if(res==-1) puts("No Solution");
	else
	{
		for(int i=1;i<=n;++i)
			printf("%.2lf\n",x[i]);
	}
	return 0;
}

异或方程组

模板

int x[maxn];
int gauss(int a[][maxn],int n,int m)
{
    int row,col;
    for(row=1,col=1; row<=n&&col<=m; ++row,++col)
    {
        int maxrow=row;
        for(int i=row+1; i<=n; ++i)
            if(abs(a[i][col])>abs(a[row][col])) maxrow=i;
        if(maxrow!=row)
        {
            for(int i=col; i<=m+1; ++i)
                swap(a[row][i],a[maxrow][i]);
        }
        if(a[row][col]==0)
        {
            row--;
            continue;
        }
        for(int i=row+1; i<=n; ++i)
        {
            if(a[i][col]!=0)
            {
                for(int j=col; j<=m+1; ++j)
                    a[i][j]^=a[row][j];
            }
        }
    }
    for(int i=row; i<=n; ++i)
        if(a[i][col]!=0) return -1;//存在无解的情况 
    if(row<m+1) return m-row+1;//返回自由变元的数量 

    for(int i=m; i>=1; --i)
    {
        int res=a[i][m+1];
        for(int j=i+1; j<=m; ++j)
            if(a[i][j]!=0) res^=(a[i][j]&&x[j]);//&&替代了 *
        x[i]=(res&&a[i][i]);
    }
}

开关问题 POJ - 1830

链接:http://poj.org/problem?id=1830

题意:给定 n 盏灯的初始状态和最终状态,每次开关一盏灯都会引起相关的灯的变化,问有多少种方案从初始状态到达最终状态

思路:高斯消元,第 i 行表示第 i 盏灯受 1 ~ n 个开关的影响情况。

  • 比如, 1 0 1 1 1 表示当前灯受 x 1 , x 3 , x 4 x_1,x_3,x_4 x1,x3,x4 三盏灯的影响,并且累积状态发生了改变
  • 最终答案就是求自由变元的个数 x x x 2 x 2^x 2x 就是答案
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#define ll long long
using namespace std;
const int maxn=35;

int guess(int a[][maxn],int n,int m)
{
    int row,col;
    for(row=1,col=1; row<=n&&col<=m; ++row,++col)
    {
        int maxrow=row;
        for(int i=row+1; i<=n; ++i)
            if(abs(a[i][col])>abs(a[row][col])) maxrow=i;
        if(maxrow!=row)
        {
            for(int i=col; i<=m+1; ++i)
                swap(a[row][i],a[maxrow][i]);
        }
        if(a[row][col]==0)
        {
            row--;
            continue;
        }
        for(int i=row+1; i<=n; ++i)
        {
            if(a[i][col]!=0)
            {
                for(int j=col; j<=m+1; ++j)
                    a[i][j]^=a[row][j];
            }
        }
    }
    for(int i=row; i<=n; ++i)
        if(a[i][col]!=0) return -1;
    return m-row+1;
}
int t,n,s[maxn],e[maxn],a[maxn][maxn];
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        for(int i=1; i<=n; ++i) scanf("%d",&s[i]);
        for(int i=1; i<=n; ++i) scanf("%d",&e[i]);
        memset(a,0,sizeof(a));
        int u,v;
        while(scanf("%d%d",&u,&v)&&(u||v)) a[v][u]=1;
        for(int i=1; i<=n; ++i) a[i][i]=1;
        for(int i=1; i<=n; ++i) a[i][n+1]=s[i]^e[i];
        int res=guess(a,n,n);
        if(res==-1) puts("Oh,it's impossible~!!");
        else printf("%d\n",(1<<res));
    }
    return 0;
}

EXTENDED LIGHTS OUT

链接:http://poj.org/problem?id=1222

题意:给出一个 5 × 6 5 \times 6 5×6 的 01 矩阵,每次选择一个位置 ( i , j ) (i,j) (ij) 会翻转与它相邻的 4 个点和它自己,请你输出一种方案,使得矩阵全部翻转为 0

思路: 类比上一题,30盏灯,每 i 行表示第 i 盏灯受到的其他灯的影响情况,最后一个值 val 表示是否需要改变。

#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#define ll long long
using namespace std;
const int maxn=35;

int x[maxn];
int gauss(int a[][maxn],int n,int m)
{
    int row,col;
    for(row=1,col=1; row<=n&&col<=m; ++row,++col)
    {
        int maxrow=row;
        for(int i=row+1; i<=n; ++i)
            if(abs(a[i][col])>abs(a[row][col])) maxrow=i;
        if(maxrow!=row)
        {
            for(int i=col; i<=m+1; ++i)
                swap(a[row][i],a[maxrow][i]);
        }
        if(a[row][col]==0)
        {
            row--;
            continue;
        }
        for(int i=row+1; i<=n; ++i)
        {
            if(a[i][col]!=0)
            {
                for(int j=col; j<=m+1; ++j)
                    a[i][j]^=a[row][j];
            }
        }
    }
    for(int i=row; i<=n; ++i)
        if(a[i][col]!=0) return -1;//存在无解的情况 
    if(row<m+1) return m-row+1;//返回自由变元的数量 

    for(int i=m; i>=1; --i)
    {
        int res=a[i][m+1];
        for(int j=i+1; j<=m; ++j)
            if(a[i][j]!=0) res^=(a[i][j]&&x[j]);//&&替代了 *
        x[i]=(res&&a[i][i]);
    }
}
int t,a[maxn][maxn];
int main()
{
    int Case=0;
    scanf("%d",&t);
    while(t--)
    {
        memset(a,0,sizeof(a));
        memset(x,0,sizeof(x));
        for(int i=1; i<=30; ++i) scanf("%d",&a[i][31]);
        for(int i=1; i<=5; ++i)
        {
            for(int j=1; j<=6; ++j)
            {
                int col=(i-1)*6+j;
                a[col][col]=1;
                if(i>1) a[(i-2)*6+j][col]=1;
                if(i<5) a[i*6+j][col]=1;
                if(j>1) a[(i-1)*6+j-1][col]=1;
                if(j<6) a[(i-1)*6+j+1][col]=1;
            }
        }
        gauss(a,30,30);
        printf("PUZZLE #%d\n",++Case);
        for(int i=1; i<=30; ++i)
            printf("%d%c",x[i],i%6==0?'\n':' ');
    }
    return 0;
}

你可能感兴趣的:(数学,线性代数)