高斯消元——解线性方程组+球形空间产生器+开关问题
传送门:https://www.acwing.com/activity/content/11/
思路:
把原矩阵变成阶梯型矩阵解题步骤:
1.找到绝对值最大的一行。
2.将该行和最上面未处理好的一行交换位置
3.将该行第一个数变成1
4.将1这一列下面的所有数变成0;
代码:
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int N=110,M=5e4+10;
const double eps=1e-6;
int n;
double a[N][N];
int gauss()
{
int c,r;
for(c=0,r=0;c<n;c++)
{
int t=r;
for(int i=r;i<n;i++)
if(fabs(a[i][c])>fabs(a[t][c])) ///找到绝对值最大的一行
t=i;
if(fabs(a[t][c])<eps) continue; ///假如该列所有数都是0了就跳过这一列,从当前行的下一列开始处理
for(int i=c;i<=n;i++) swap(a[t][i],a[r][i]); ///和现在的第一行交换位置
for(int i=n;i>=c;i--) a[r][i]/=a[r][c]; ///从后往前处理让该行第一个数变成1
for(int i=r+1;i<n;i++)
if(fabs(a[i][c])>eps) ///只要1下面的数不是0,就把该列下面的每一个数都变成0
for(int j=n;j>=c;j--)
a[i][j]-=a[r][j]*a[i][c]; ///上面有个1,所以0右边的数只要减去的最左边的数乘上对应列数的数即可
r++;
}
if(r<n) ///如果最后方程的数量不等于n,要么无解,要么无穷解
{
for(int i=r+1;i<n;i++)
if(fabs(a[i][n])>eps) ///出现0!=0的情况就无解
return 2;
return 1;
}
for(int i=n-1;i>=0;i--) ///这里一步是将阶梯型矩阵的最后一位变成约化阶梯型矩阵下的结果。
for(int j=i+1;j<n;j++)
a[i][n]-=a[i][j]*a[j][n];
return 0;
}
int main()
{
cin>>n;
for(int i=0;i<n;i++)
for(int j=0;j<=n;j++)
cin>>a[i][j];
int t=gauss();
if(t==0)
{
for(int i=0;i<n;i++) printf("%.2lf\n",a[i][n]);
}else if(t==1) puts("Infinite group solutions");
else puts("No solution");
return 0;
}
传送门:207. 球形空间产生器 - AcWing题库
思路:在一个普通的二维平面上的球的坐标是(x,y),可以用三个点来确定,公式就是
(a1-x)^2+(b1-y1)^2=R^2 (a2-x)^2+(b2-y1)^2=R^2 (a3-x)^2+(b3-y1)^2=R^2
扩展到n维的时候就需要用一个n元组来表示圆心[x1,x2,x3...,xn],用n+1个点的坐标确定一个圆:有如下
(a01-x1)^2+(a02-x2)^2+(a03-x3)^2+...+(a0n-xn)^2=R^2 第0条
(a11-x1)^2+(a12-x2)^2+(a13-x3)^2+...+(a1n-xn)^2=R^2 第1条
(a21-x1)^2+(a22-x2)^2+(a23-x3)^2+...+(a2n-xn)^2=R^2 第2条
.
(an1-x1)^2+(an2-x2)^2+(an3-x3)^2+...+(ann-xn)^2=R^2 第n条
高斯消元所用的是线性方程组,这里要用n条方程都减去第0条得到n条线性方程。
以第一条减去第二条为例,相减并移项后有如下
2(a11-a01)x1+2(a12-a02)x2+...+2(a1n-a0n)xn==a11^2+a12^2+...a1n^2-a01^2+a02^2-...a0n^2
此时x1到xn这n个数以外都是常数。
代码:
#include <iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
const int N=15;
const double eps=1e-6;
int n;
double a[N][N];
double b[N][N];
void gauss()
{
for(int c=1,r=1;c<=n;r++,c++)
{
int t=r;
for(int i=r+1;i<=n;i++)
if(fabs(b[i][c])>fabs(b[t][c])) t=i;
for(int i=c;i<=n+1;i++) swap(b[t][i],b[r][i]);
for(int i=n+1;i>=c;i--) b[r][i]/=b[r][c];
for(int i=r+1;i<=n;i++)
for(int j=n+1;j>=c;j--)
b[i][j]-=b[i][c]*b[r][j];
}
//转化为约化阶梯型矩阵
for(int i=n;i>1;i--)
for(int j=i-1;j;j--)
{
b[j][n+1]-=b[i][n+1]*b[j][i];
b[j][i]=0;
}
}
int main()
{
scanf("%d",&n);
for(int i=0;i<=n;i++)
for(int j=1;j<=n;j++)
scanf("%lf",&a[i][j]);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
b[i][j]+=2*(a[i][j]-a[0][j]);
b[i][n+1]+=a[i][j]*a[i][j]-a[0][j]*a[0][j];
}
gauss();
for(int i=1;i<=n;i++)
printf("%.3lf ",b[i][n+1]);
return 0;
}
传送门:208. 开关问题 - AcWing题库
思路:将原问题转化成一个解异或线性方程组的问题;
用一个异或线性方程表示一个灯的状态的改变,每个开关怎么按可以使这条式子的对应开关产生怎么的变化就是一个异或的关系。
先从简单的样例入手,现在设一个三元组[x1,x2,x3]分别表示三个开始有没有产生按。
现在有1可以影响2, 2 可以影响1,2,3 ,3可以影响3 ,初始态为0,0,0,结果态为1,1,1,可以列出以下的三个方程
x1^x2 =x1(使得x1如何变化) ==> 1 1 0 1
x1^x2 =x2(使得x2如何变化) ==> 1 1 0 1
x2^x3 =x3(使得x3如何变化) ==> 0 1 1 1
只要上面的异或线性方程组有解,那么原问题就一定有解,如果有自由变量的话,因为么一个灯的状态只有两个,故有k个自由变量就有2^k个解决方案。
代码:
#include <iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
const int N=36;
const double eps=1e-6;
int n;
int a[N][N];
int gauss()
{
int r,c;
for(r=1,c=1;c<=n;c++)
{
int t=r;
for(int i=r+1;i<=n;i++)
if(a[i][c])
t=i;
if(!a[t][c])continue;
for(int i=c;i<=n+1;i++) swap(a[t][i],a[r][i]);
for(int i=r+1;i<=n;i++)
for(int j=n+1;j>=c;j--)
a[i][j]^=a[i][c]&a[r][j];
r++;
}
int res=1;
if(r<n+1)
{
for(int i=r;i<=n;i++)
{
if(a[i][n+1]) return -1;
res*=2;
}
}
return res;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
memset(a,0,sizeof a);
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i][n+1]);
for(int i=1;i<=n;i++)
{
int m;
scanf("%d",&m);
a[i][n+1]^=m;
a[i][i]=1;
}
int x,y;
while(scanf("%d%d",&x,&y),x||y)
{
a[y][x]=1;
}
int t=gauss();
if(t==-1) puts("Oh,it's impossible~!!");
else
printf("%d\n",t);
}
return 0;
}