概念
- 自由变元:取非零行的首非零元所在列对应的变元为约束变元,其余变元取作自由变元
比如 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) (i,j) 会翻转与它相邻的 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;
}