P7112题解&浅谈行列式
题意简述
模板题,求一个
题目分析
这个题的最终代码其实很简单,重点在于过程。说实话,我在做这个题之前也就只知道个行列式的定义,只会暴力硬算。做了这个题才了解了行列式有那么多的好的性质,也算是开了开眼界吧。所以本文先介绍行列式本身再讲解题步骤。由于这篇文章本身也是我在学习 @Reywmp 大佬的 数学(5)——线性代数:行列式 一文之后写的,所以本文参考了这篇文章的内容,在此先膜拜大佬。另外我个人十分重视性质的证明过程,所以原来打算把自己写的几个性质的证明直接放到文中,后来考虑重要性不高以及篇幅问题就写到剪贴板里了,读者可以选择性看一看。(最好看看吧,毕竟我肝了将近两个小时)
定义
首先来看行列式的定义。对于一个
其中
形象化一点,你可以认为
那么这个看起来有点抽象的东西有什么实际用途呢?一个经典的例子是这样的:
性质
行列式的性质有很多,在这里只介绍较为有用的几个。
1. 交换矩阵的两行或两列,行列式取反。
2. 如果矩阵的某两行(列)相同,行列式为 0。
3. 矩阵的某一行(列)的每个元素乘(除)
推论:由性质 2 和 3 可得到:若矩阵中有两行(列)的所有元素成比例,则其行列式为 0。
4. 若矩阵
推论:若矩阵
5.把矩阵的某一行(列)乘上一个系数
6. 矩阵的行列式等于其任意一行(列)的每个元素及其代数余子式的乘积之和
先解释下什么是代数余子式。我们称一个矩阵去掉第
那么原命题就是对于第
对于第
解题步骤
本题直接暴力求值的时间复杂度为
考虑之前的性质 1、3、5 中的操作,形式是不是有点熟悉?没错,它们就是矩阵的初等变换。而在高斯消元中,我们可以通过初等行变换进行消元将一个矩阵变为一个上三角矩阵。所以,我们只要能够求出消元后的上三角矩阵的行列式,就可以很简单地计算出原矩阵的行列式。
所以问题来了,怎么快速计算上三角矩阵的行列式?举个例子:
根据性质 6,有
注意到题目中
回忆
答案是肯定的。我们选定需要消元的那一行,与其他行做辗转相除。因为辗转相除所用的操作都是矩阵的初等变换,所以根据消元后的矩阵的行列式可以运用性质 1、3、5 很方便计算出原矩阵的行列式。
最后看一下算法的时间复杂度。消元部分的时间复杂度为
代码实现
#include<bits/stdc++.h> using namespace std; int n,a[610][610],res=1,w=1/*初等变换带来的系数*/,mod; int main() { scanf("%d%d",&n,&mod); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) scanf("%d",&a[i][j]); for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++) { while(a[i][i])//没消掉就继续消 { for(int rate=a[j][i]/a[i][i]/*“相除”,把商作为系数乘在第 i 行的元素上*/,k=i/*没消掉的元从 i 开始*/;k<=n;++k) a[j][k]=(a[j][k]-1ll*rate*a[i][k]%mod+mod)%mod;//在消掉 a[j][i] 的同时它的整行元素都要进行同样操作 swap(a[i],a[j]);//“辗转” w=-w;//计算系数 } swap(a[i],a[j]);//“辗转” w=-w;//计算系数 } for(int i=1;i<=n;i++) res=1ll*a[i][i]*res%mod;//计算上三角矩阵的行列式 res=(1ll*res*w%mod+mod)%mod;//一定要乘上系数!!! printf("%d",res); return 0; }
由于本文写的时间跨度比较长,不可避免地存在错误和不足,欢迎大家指出!
update
本文最初发表于 2023.6.28,后自己发现文中的几处笔误,故于 2023.8.9 重新修改上传。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 记一次.NET内存居高不下排查解决与启示