高斯消元:
求解线性方程组的方法。
主体:将系数提出来,形成一个系数矩阵。将等号右边的常数提出来,形成一个常数矩阵。然后加减消元,带入消元。
步骤:
1.明确要消去的元的位置pos,将某一行有这个元(即系数不为0)的方程提出来,这一行记作 i ,对应的元的系数记作x。
2.现在把第 i 行去消第 j 行(目的是把第 j 行的pos处系数变为0),记原系数为y。将第 i 行同 ÷ y,再*x ,就构造出了pos处系数为x的另一个方程。
3.两式相减,就可以把pos处消成0啦。
最后答案:
通过带入消元法最后处理出的是一个阶梯矩阵(对角线上有值),答案即为常数÷对角线上的值啦。
那有没有无穷多解或无解的方程呢?
1.当0=d是说明无解
2.当存在一行所有的系数都为0,即有无穷多个解,缺少的那个位置为自由元,可以取任意值,其他 值可以固定 的变量叫主元。 这样的方程组有无穷多解。
参考:算法与竞赛指南:P155~160
//待补图!!!
//P3389 【模板】高斯消元法 #include<bits/stdc++.h> using namespace std; #define ri register int #define N 105 #define eps 1e-8 double b[N],c[N][N]; int main() { int n; scanf("%d",&n); for(ri i=1;i<=n;i++){ for(ri j=1;j<=n;j++) scanf("%lf",&c[i][j]);//系数矩阵 scanf("%lf",&b[i]);//常数矩阵 } for(int i=1;i<=n;i++){ for(int j=i;j<=n;j++) if(fabs(c[j][i])>eps) swap(c[i],c[j]),swap(b[i],b[j]);//把非0的一行去消其他行 swap使得其变成当前行 //找到一个系数全为0的式子 说明存在自由元 方程有无穷多个解 if(fabs(c[i][i])<eps) { printf("No Solution "); return 0; } for(int j=1;j<=n;j++){//这里枚举行 将每一行的第i个数以后的数加减消元 if(i==j) continue; double r=c[j][i]/c[i][i];//通分 for(int k=i;k<=n;k++) c[j][k]-=r*c[i][k];//第j行的每一个数消去对应的值 b[j]-=b[i]*r;//常数项也要变 } } for(int i=1;i<=n;i++) printf("%.2lf ",b[i]/c[i][i]); } /* 3 1 2 -1 3 1 2 -4 0 -1 -2 6 2 */
(只是根据题意处理了一下系数矩阵和常数矩阵而已)
#include<bits/stdc++.h> using namespace std; #define N 15 #define ri register int #define eps 1e-8 int n; double a[N][N],c[N][N],b[N]; int main() { scanf("%d",&n); for(ri i=1;i<=n+1;++i) for(ri j=1;j<=n;++j) scanf("%lf",&a[i][j]); for(ri i=1;i<=n;++i) for(ri j=1;j<=n;++j){ c[i][j]=2*(a[i+1][j]-a[i][j]); b[i]+=a[i+1][j]*a[i+1][j]-a[i][j]*a[i][j]; } for(ri i=1;i<=n;++i){ for(ri j=i;j<=n;++j) if(fabs(c[j][i])>eps) swap(c[j],c[i]),swap(b[j],b[i]); for(ri j=1;j<=n;++j){ if(i==j) continue; double r=c[j][i]/c[i][i]; for(ri k=i;k<=n;++k) c[j][k]-=c[i][k]*r; b[j]-=b[i]*r; } } for(ri i=1;i<=n;++i) printf("%.3lf ",b[i]/c[i][i]); } /* 2 0.0 0.0 -1.0 1.0 1.0 0.0 */
求异或方程模板:开关问题
分析:
最难的地方在于灯的关联性
因为每一个开关只能操作一次, 就可以把一个灯是否操作看做0/1的变量, 通过列方程来求解。
而一个开关关联了另一个开关, 即意味着在将原始状态变成理想状态的过程中 要xi^xj 。
问题就转换成:有n个方程, 每个方程有n个异或值:a(i,j)^xj 。
另外:a(i,i)=1(自己与自己肯定是相关联的) 。
解释一下每个变量的意义:在第i个方程中, 第j个是否与i关联:a(i,j) ,求它是否会被操作:xj 。
而它们的常数项为:st[i]^ed[i], 即初始状态^期望状态 。
在输入的时候就预处理出系数矩阵 ,然后把每一个方程二进制压位:注意最后一位是=右边的常数项 。
答案统计:
这道题求的是方案数,很明显最后的操作序列中可能会存在这样的数:取0也可以, 取1也可以。
这样的数就是普通高斯消元中的自由元,由于每个数都有0和1两种选择 ,则答案为2^cnt ,cnt为自由元的个数 。
注意代码具体实现中的细节
//#include<bits/stdc++.h> #include <iostream> #include <cstdio> #include <cstring> using namespace std; #define ri register int #define N 55 int a[N],ans=0,n; /*void get(int x)//输出对应的二进制数 { int tmp[10],cnt=0; memset(tmp,0,sizeof(tmp)); while(x){ cnt++; if(x&1) tmp[cnt]=1; x>>=1; } for(int i=n+1;i>=1;i--) printf("%d",tmp[i]); printf(" "); }*/ int main() { int T,x,y; scanf("%d",&T); while(T--) { memset(a,0,sizeof(a)); scanf("%d",&n); for(ri i=1;i<=n;++i) scanf("%d",&a[i]); for(ri i=1;i<=n;++i) scanf("%d",&x),a[i]^=x,a[i]|=(1<<i); while(scanf("%d%d",&x,&y)){ if(x==0 && y==0) break; a[y]|=(1<<x);//这里y和x不要弄反!!! //a[i]表示的是 二进制下 一系列操作的异或值 能取到最低位的0/1 的系数矩阵 //而x y 表示操作x y会随之变化 //即在使第y个灯变成最终值时 方程中第x位的系数是1 所以x和y不能反着写!!! } ans=1; for(ri i=1;i<=n;++i){ for(ri j=i+1;j<=n;++j) if(a[j]>a[i]) swap(a[i],a[j]);//取的是首位最大值的a[i] if(a[i]==0) { ans=1<<(n-i+1); break; }//即全部为0 说明剩下的方程系数全为0 即有n-i+1个自由元 if(a[i]==1) { ans=-1; break; }//最后一项=1 前面都=0 即出现了无解的情况 for(ri k=n;k>=1;--k) if(a[i]>>k & 1){//每次找到其最高位 去消其他的方程 for(ri j=1;j<=n;++j) if( i!=j && a[j]>>k & 1) a[j]^=a[i];//只消第k位有值的方程 //原因:每一次固定消最高位 如果其它位这一位为0 说明没有这个未知数 是不应该被消的 break;//!!! } } if(ans==-1) printf("Oh,it's impossible~!! "); else printf("%d ",ans); } }