线性代数
高斯消元
高斯消元适用于求解线性方程。
线性方程
形如
的方程,我们称之为线性方程。
系数矩阵
我们可以将它们的系数提出来组成一个矩阵,我们称之为系数矩阵。如下。
矩阵的行变换(列变换)
1.将矩阵的第 行和第 行元素互换;
2.将矩阵的第 行元素分别乘 ;
3.将矩阵的第 行元素分别乘 再加到第 行的对应位置。
由于矩阵可以倒置,行变换与列变换等价。
增广矩阵
定义:系数矩阵右侧加一列,这一列为方程等式右边的数值。
如下。
行阶梯矩阵
定义:非零行的第一个非零元素的列下标随着行数增加而严格递增,全零元素的行在末尾。
如下。
方程组解的判定
矩阵的行秩(rank):行阶梯矩阵非零行的数量 ;
1.有唯一解:增广矩阵行秩 未知数数量;
2.有无数多组解:增广矩阵的行秩 未知数的数量;
3.无解:系数矩阵的行秩 增广矩阵的行秩。
复杂度
例一 P3389【模板】高斯消元法
点击查看代码
#include<bits/stdc++.h> using namespace std; const int MAXN=105; int n; double a[MAXN][MAXN]; 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]); for(int i=1;i<=n;i++) { int maxn=i; for(int j=i+1;j<=n;j++) if(abs(a[j][i])>abs(a[maxn][i])) maxn=j; for(int j=1;j<=n+1;j++) swap(a[i][j],a[maxn][j]); if(fabs(a[i][i])<0.001) {printf("No Solution\n");return 0;} for(int j=1;j<=n;j++) { if(i==j) continue; double rate=a[j][i]*1.0/a[i][i]; for(int k=1;k<=n+1;k++) a[j][k]-=a[i][k]*rate; } } for(int i=1;i<=n;i++) { double ans=a[i][n+1]*1.0/a[i][i]; printf("%.2lf\n",ans); } return 0; }
例二 P2455 [SDOI2006]线性方程组
真正意义上的高斯消元模板题,将三种情况完全分开了。
点击查看代码
#include<bits/stdc++.h> using namespace std; const int MAXN=105; const double eps=1e-6; int n; double a[MAXN][MAXN]; 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]); for(int i=1;i<=n;i++) { int maxn=i; for(int j=i+1;j<=n;j++) if(abs(a[j][i])>abs(a[maxn][i])) maxn=j; for(int j=1;j<=n+1;j++) swap(a[i][j],a[maxn][j]); if(fabs(a[i][i])<eps) continue; for(int j=1;j<=n;j++) { if(i==j) continue; double rate=a[j][i]*1.0/a[i][i]; for(int k=1;k<=n+1;k++) a[j][k]-=a[i][k]*rate; } } for(int i=1;i<=n;i++) { double sum=0; for(int j=1;j<=n;j++) sum+=a[i][j]; if(sum==0 && fabs(a[i][n+1])>=eps) {printf("-1\n");return 0;} } for(int i=1;i<=n;i++) if(fabs(a[i][i])<=eps && fabs(a[i][n+1])<=eps) {printf("0\n");return 0;} for(int i=1;i<=n;i++) { double ans=a[i][n+1]*1.0/a[i][i]; printf("x%d=%.2lf\n",i,ans); } return 0; }
例三 P3164 [CQOI2014]和谐矩阵
只是把高斯消元用在了异或上。
那么我们就将系数全设为 。
点击查看代码
#include<bits/stdc++.h> using namespace std; const int MAXN=55; inline int read() { int x=0,f=1;char ch=getchar(); while(!isdigit(ch)) { if(ch=='-') f=-1;ch=getchar(); } while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return x*f; } int dx[]={0,0,0,1,-1},dy[]={0,-1,1,0,0}; int n,m,id[MAXN][MAXN],ans[MAXN*MAXN]; bitset<MAXN*MAXN>a[MAXN*MAXN]; inline void solve() { for(int i=1;i<=n*m;i++) { for(int j=i;j<=n*m;j++) if(a[j][i]>0) {swap(a[i],a[j]);break;} if(!a[i][i]) ans[i]=1; for(int j=i+1;j<=n*m;j++) if(a[j][i]) a[j]^=a[i]; } for(int i=n*m;i>=1;i--) for(int j=i+1;j<=n*m;j++) ans[i]^=(ans[j]*a[i][j]); return; } int main() { n=read(),m=read(); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) id[i][j]=(i-1)*m+j; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) for(int k=0;k<=4;k++) { int x=i+dx[k],y=j+dy[k]; if(x<1 || y<1 || x>n || y>m) continue; a[id[i][j]][id[x][y]]=1; } solve(); for(int i=1;i<=n*m;i++) { printf("%d ",ans[i]); if(!(i%m)) puts(""); } return 0; }
例四 P4035 [JSOI2008]球形空间产生器
其实也是板子题,就是用每相邻两个凑一个方程,因为刚好给的 个点。
点击查看代码
#include<bits/stdc++.h> using namespace std; const int MAXN=105; double a[MAXN][MAXN]; double ans[MAXN]; double eps=1e-7; int n; int main() { scanf("%d",&n);n++; for(int i=1;i<=n;i++) { for(int j=1;j<n;j++) { scanf("%lf",&a[i][j]); a[i][n+1]-=a[i][j]*a[i][j];a[i][j]*=2.0; } a[i][n]=1; } for(int i=1;i<=n;i++) { int maxn=i; for(int j=i+1;j<=n;j++) if(fabs(a[maxn][i])<fabs(a[j][i])) maxn=j; if(fabs(a[maxn][i])<eps) return 0; if(i!=maxn) swap(a[i],a[maxn]); double rate=a[i][i]; for(int j=i;j<=n+1;j++) a[i][j]/=rate; for(int j=i+1;j<=n;j++) { rate=a[j][i]; for(int k=i;k<=n+1;k++) a[j][k]-=a[i][k]*rate; } } ans[n]=a[n][n+1]; for(int i=n-1;i>=1;i--) { ans[i]=a[i][n+1]; for(int j=i+1;j<=n;j++) ans[i]-=(a[i][j]*ans[j]); } for(int i=1;i<n;i++) printf("%.3lf ",-ans[i]); return 0; }
例五 P2962 [USACO09NOV]Lights G
这是一道解异或方程的题。。。
点击查看代码
#include<bits/stdc++.h> using namespace std; const int MAXN=105; inline int read() { int x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();} while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return x*f; } int n,m; bool a[MAXN][MAXN]; inline bool solve() { bool flag=true; for(int i=1;i<=n;i++) { int maxn=i; while(maxn<=n && a[maxn][i]==0) maxn++; if(maxn>n) {flag=false;continue;} swap(a[i],a[maxn]); for(int j=1;j<=n;j++) { if(i==j || a[j][i]==0) continue; for(int k=i;k<=n+1;k++) a[j][k]^=a[i][k]; } } return flag; } bool vis[MAXN]; int ans=1e9; inline void dfs(int x,int num) { if(num>=ans) return; if(x==0) {ans=num;return;} if(a[x][x]) { bool val=a[x][n+1]; for(int i=x+1;i<=n;i++) if(a[x][i]) val^=vis[i]; dfs(x-1,num+val); } else { dfs(x-1,num);vis[x]=1; dfs(x-1,num+1);vis[x]=0; } return; } int main() { n=read(),m=read(); for(int i=1;i<=n;i++) a[i][i]=a[i][n+1]=1; for(int i=1;i<=m;i++) { int x=read(),y=read(); a[x][y]=a[y][x]=1; } if(solve()==true) { ans=0; for(int i=1;i<=n;i++) ans+=a[i][n+1]; printf("%d\n",ans); } else { dfs(n,0); printf("%d\n",ans); } return 0; }
线性基
向量
在信息学中可以理解为一个数列。
eg: 是一个三维向量。
向量的加法
eg:
向量的数乘
eg
一些概念
1.线性空间
定义:设 个向量通过线性组合能够表示出的所有向量的集合。
2.线性相关
定义:设一组向量 ,若其中存在一个向量 可以由其余向量线性表示出来,则称该组向量线性相关。
3.线性无关
定义:相对的,设一组向量 ,若其中任意一个向量 都不能由其余向量线性表示出来,则称该组向量线性无关。
4.线性基底
定义:设一组向量存在一个子集,该子集的向量是线性无关的,且该子集和原向量能表示出相同的线性空间,则称该子集为该组向量的一组线性基。
求法
将一个向量视为矩阵的一行,则 个 维的向量可以表示成一个 行 列的矩阵。
利用矩阵行变换,化简为行阶梯矩阵,则非零行向量就是一组线性基。
时间复杂度较高,为
例一 P3265 [JLOI2015]装备购买
1. 个 维的向量;
2.找出其中最便宜的一组线性基。
点击查看代码
#include<bits/stdc++.h> using namespace std; const double eps=1e-5; const int MAXN=505; inline int read() { int x=0,f=1;char ch=getchar(); while(!isdigit(ch)) {if(ch=='-')f=-1; ch=getchar();} while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return x*f; } struct node { double a[MAXN]; int val; inline bool operator<(const node x)const { return val<x.val; } }x[MAXN]; int y[MAXN],n,m,ans,cnt; int main() { n=read(),m=read(); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) scanf("%lf",&x[i].a[j]); for(int i=1;i<=n;i++) x[i].val=read(); sort(x+1,x+1+n); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { if(abs(x[i].a[j])<=eps)continue; if(!y[j]) { y[j]=i;cnt++; ans+=x[i].val; break; } else { double rat=x[i].a[j]/x[y[j]].a[j]; for(int k=j;k<=m;k++) x[i].a[k]-=rat*x[y[j]].a[k]; } } printf("%d %d",cnt,ans); return 0; }
例二 P3812【模板】线性基
模板,我们常用构造法。采用二进制拆位的思路。
点击查看代码
#include<bits/stdc++.h> #define int long long using namespace std; const int MAXN=65; inline int read() { int x=0,f=1;char ch=getchar(); while(!isdigit(ch)) { if(ch=='-') f=-1;ch=getchar(); } while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return x*f; } int vis[MAXN];//基底的集合,p[i]存储在二进制数位i上贡献1的元素。 int n,ans; inline void check(int x)//将x尝试插入基底的集合 { for(int i=63;i>=0;i--) { if(x>>i) if(vis[i]==0) {vis[i]=x;break;} else x=x^vis[i]; } return; } signed main() { n=read(); for(int i=1;i<=n;i++) { int x=read(); check(x); } for(int i=63;i>=0;i--) ans=max(ans,ans^vis[i]); printf("%lld\n",ans); return 0; }
本文作者:Code_AC
本文链接:https://www.cnblogs.com/code-ac/p/16861339.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步