高斯消元习题

概念

  • 自由变元:取非零行的首非零元所在列对应的变元为约束变元,其余变元取作自由变元
    比如 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列,即除数的位置,从而减小误差

浮点数模板

int gauss(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(fabs(a[i][col])>fabs(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=m; 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;
}

P3389 【模板】高斯消元法

在这里插入图片描述链接: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 gauss(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=m;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=gauss(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 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;
    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=gauss(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;
}

枚举写法

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
int t,x;
int origin[10],ans[10],light[10];
void flip(int &x,int pos)
{
    x^=(1<<pos-1);
}
int main()
{
    scanf("%d",&t);
    int Cas=0;
    while(t--)
    {
        memset(origin,0,sizeof(origin));
        for(int i=1;i<=5;++i)
        {
             for(int j=1;j<=6;++j)
             {
                 scanf("%d",&x);
                 if(x==1) origin[i]|=(1<<j-1);  
             }                       
        }
        for(int i=0;i<64;++i)
        {
            memcpy(light,origin,sizeof(origin));
            int choose=i;
            for(int j=1;j<=5;++j)
            {
                ans[j]=choose;
                for(int k=1;k<=6;++k)
                {
                    if(choose>>k-1&1)
                    {
                        if(k>1) flip(light[j],k-1);
                        flip(light[j],k);
                        if(k<6) flip(light[j],k+1);
                    }
                }
                if(j<5)
                    light[j+1]^=choose;               
                choose=light[j];
            }
            if(light[5]==0)
            {
                printf("PUZZLE #%d\n",++Cas);
                for(int j=1;j<=5;++j)
                    for(int k=1;k<=6;++k)
                        printf("%d%c",ans[j]>>k-1&1,k==6?'\n':' ');
                break;
            }
        }   
    }
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值