一、高斯消元
1.高斯消元解线性方程组
求解这个方程组。
下图为一个包含
m
m
m 个方程
n
n
n 个末知数的线性方程组示例:
a
11
x
1
+
a
12
x
2
+
⋯
+
a
1
n
x
n
=
b
1
a
21
x
1
+
a
22
x
2
+
⋯
+
a
2
n
x
n
=
b
2
⋯
⋯
⋯
⋯
⋯
⋯
⋯
⋯
⋯
⋯
⋯
a
m
1
x
1
+
a
m
2
x
2
+
⋯
+
a
m
n
x
n
=
b
m
}
\left.\begin{array}{c} a_{11} x_{1}+a_{12} x_{2}+\cdots+a_{1 n} x_{n}=b_{1} \\ a_{21} x_{1}+a_{22} x_{2}+\cdots+a_{2 n} x_{n}=b_{2} \\ \cdots \cdots \cdots \cdots \cdots \cdots \cdots \cdots \cdots \cdots \cdots \\ a_{m 1} x_{1}+a_{m 2} x_{2}+\cdots+a_{m n} x_{n}=b_{m} \end{array}\right\}
a11x1+a12x2+⋯+a1nxn=b1a21x1+a22x2+⋯+a2nxn=b2⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯am1x1+am2x2+⋯+amnxn=bm⎭⎪⎪⎬⎪⎪⎫
样例:
{
x
1
+
2
x
2
−
x
3
=
6
2
x
1
+
x
2
−
3
x
3
=
−
9
−
x
1
−
x
2
+
x
3
=
7
\left\{\begin{array}{c} x_1+2x_2-x_3=6 \\ 2x_1+x_2-3x_3=-9 \\ -x_1-x_2+x_3=7 \end{array}\right.
⎩⎨⎧x1+2x2−x3=62x1+x2−3x3=−9−x1−x2+x3=7
矩阵的初等行变换:
(1)换法变换:对换矩阵的两行。
(2)倍法变换:用非零数乘矩阵某一行的每个元素。
(3)消法变换:用数乘矩阵某一行的每个元素后加到另一行的对应元素上。
方程组的解:
系数矩阵: A = [ a 11 a 12 … a 1 n a 21 a 22 … a 2 n … … … … a m 1 a m 2 … a m n ] A=\left[\begin{array}{cccc}a_{11} & a_{12} & \ldots & a_{1 n} \\ a_{21} & a_{22} & \ldots & a_{2 n} \\ \ldots & \ldots & \ldots & \ldots \\ a_{m 1} & a_{m 2} & \ldots & a_{m n}\end{array}\right] A=⎣⎢⎢⎡a11a21…am1a12a22…am2…………a1na2n…amn⎦⎥⎥⎤
增广矩阵:
R
(
A
,
b
)
=
[
a
11
a
12
…
a
1
n
b
1
a
21
a
22
…
a
2
n
b
2
…
…
…
…
…
a
m
1
a
m
2
…
a
m
n
b
n
]
R(A, b)=\left[\begin{array}{cccc:c}a_{11} & a_{12} & \ldots & a_{1 n} & b_{1} \\ a_{21} & a_{22} & \ldots & a_{2 n} & b_{2} \\ \ldots & \ldots & \ldots & \ldots & \ldots \\ a_{m 1} & a_{m 2} & \ldots & a_{m n} & b_{n}\end{array}\right]
R(A,b)=⎣⎢⎢⎡a11a21…am1a12a22…am2…………a1na2n…amnb1b2…bn⎦⎥⎥⎤
- 有唯一解(完美阶梯型矩阵):满秩, R ( A ) = R ( A , b ) = n R(A)=R(A,b)=n R(A)=R(A,b)=n。
- 无解:
R
(
A
)
<
R
(
A
,
b
)
R(A)<R(A,b)
R(A)<R(A,b)
最后一行 0 = 6 0=6 0=6,显然矛盾,即误无解!~ - 无穷多解:
R
(
A
)
=
R
(
A
,
b
)
<
n
R(A)=R(A,b)<n
R(A)=R(A,b)<n
很显然, 0 = 0 0=0 0=0的话,则 x 4 x_4 x4这个未知数可以被其他未知数所替代,故有无穷多解!~
步骤
枚举每一列
c
c
c
- 先找出绝对值最大的那一行
- 将该行换到顶端
- 将该行的第一个数变为1
- 将下面所有行的第 c c c列消成0
- 从下往上将矩阵消成对角线矩阵
#include <iostream>
#include <cmath>
using namespace std;
const int N = 110;
const double eps = 1e-8;
int n;
double g[N][N];
int guass()
{
int r, c;
for(r = 0, c = 0; c < n; c ++ )
{
int t = r;
for(int i = r; i < n; i ++ ) //找到绝对值最大的那一行
if(fabs(g[i][c]) > fabs(g[t][c]))
t = i;
if(fabs(g[t][c]) < eps) continue;
for(int i = c; i <= n; i ++ ) swap(g[t][i], g[r][i]); //交换到顶端
for(int i = n; i >= c; i -- ) g[r][i] /= g[r][c];//将改行第一个数变为1
for(int i = r + 1; i < n; i ++ )//将下面所有行的c列消成0
if(fabs(g[i][c]) > eps)
for(int j = n; j >= c; j -- )
g[i][j] -= g[r][j] * g[i][c];
r ++ ;
}
if(r < n)
{
for(int i = r; i < n; i ++ )
if(fabs(g[i][n]) > eps)
return 1;//无解
return 2;//无穷多解
}
for(int i = n - 1; i >= 0; i -- )//从下往上消成对角线矩阵
for(int j = i + 1; j < n; j ++ )
g[i][n] -= g[i][j] * g[j][n];
return 0;
}
int main()
{
scanf("%d", &n);
for(int i = 0; i < n; i ++ )
for(int j = 0; j <= n; j ++ )
scanf("%lf", &g[i][j]);
int t = guass();
if(t == 1) puts("No solution");
else if(t == 2) puts("Infinite group solutions");
else
{
for(int i = 0; i < n; i ++ )
{
if(fabs(g[i][n]) < eps) g[i][n] = 0;
printf("%.2lf\n", g[i][n]);
}
}
return 0;
}
2. 高斯消元解异或线性方程组
步骤与解线性方程组类似,不过多赘述!
#include <iostream>
using namespace std;
const int N = 110;
int a[N][N];
int 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(a[i][c])
t = i;
if(!a[t][c]) continue;
//交换绝对值最大的一行到最顶端
for(int i = c; i <= n; i ++ ) swap(a[r][i], a[t][i]);
//从上到下进行消元
for(int i = r + 1; i < n; i ++ )
if(a[i][c])
for(int j = c; j <= n; j ++ )
a[i][j] ^= a[r][j];
r ++ ;
}
if(r < n)
{
for(int i = r; i < n; i ++ )
if(a[r][n])
return 1;//无解
return 2;//无穷多解
}
//从下到上消元
for(int i = n - 1; i >= 0; i -- )
for(int j = i + 1; j < n; j ++ )
if(a[i][j])//只有a[i][j]为1的时候才进行异或运算
a[i][n] ^= a[j][n];
return 0;//唯一解
}
int main()
{
scanf("%d", &n);
for(int i = 0; i < n; i ++ )
for(int j = 0; j < n + 1; j ++ )
scanf("%d", &a[i][j]);
int t = gauss();
if(t == 1) puts("No solution");
else if(t == 2) puts("Multiple sets of solutions");
else
{
for(int i = 0; i < n; i ++ ) printf("%d\n", a[i][n]);//输出答案
}
return 0;
}
二、求组合数
1. 求组合数 I
我们求组合数有一个特别的递推公式:
C
a
b
=
C
a
−
1
b
+
C
a
−
1
b
−
1
C_{a}^{b}=C_{a-1}^{b}+C_{a-1}^{b-1}
Cab=Ca−1b+Ca−1b−1
当
a
,
b
a,b
a,b较小时,采用递推式!
时间复杂度:
O
(
n
2
)
O(n^2)
O(n2)
相关题目:AcWing 885. 求组合数 I
#include <iostream>
using namespace std;
const int N = 2010, mod = 1e9 + 7;
int c[N][N];
void init()
{
for (int i = 0; i < N; i ++ )
{
for (int j = 0; j <= i; j ++ )
{
if (!j) c[i][j] = 1;
else
{
c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod;
}
}
}
}
int main()
{
int n;
scanf("%d", &n);
init();//预处理
while (n -- )
{
int a, b;
scanf("%d%d", &a, &b);
printf("%d\n", c[a][b]);
}
return 0;
}
2. 求组合数 II
当 a , b a,b a,b的数据非常大的时候( 1 0 5 10^5 105),我们用递推的方法就会超出数组最大限度。此时我们采用定义的方式求组合数!
C
a
b
=
a
!
b
!
(
a
−
b
)
!
C_{a}^{b}=\frac{a!}{b!(a-b)!}
Cab=b!(a−b)!a!,其实等价于求
C
a
b
≡
a
!
∗
i
n
f
a
c
t
(
b
!
)
∗
i
n
f
a
c
t
(
(
a
−
b
)
!
)
C_{a}^{b}\equiv a!*infact(b!)*infact((a-b)!)
Cab≡a!∗infact(b!)∗infact((a−b)!)
其中
i
n
f
a
c
t
(
i
)
infact(i)
infact(i)为
i
i
i的逆元!
相关题目链接:AcWing 886. 求组合数 II
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 1e5 + 10, mod = 1e9 + 7;
int fact[N], infact[N];
int qmi(int a, int b, int p)
{
int res = 1;
while (b)
{
if(b & 1) res = (LL)res * a % p;
a = (LL) a * a % p;
b >>= 1;
}
return res;
}
int main()
{
int n;
scanf("%d", &n);
fact[0] = infact[0] = 1;
for (int i = 1; i < N; i ++ )
{
fact[i] = (LL)fact[i - 1] * i % mod;\\fact[i]:i的阶乘
infact[i] = (LL)infact[i - 1] * qmi(i, mod - 2, mod) % mod;\\infact[i]:i的逆元的阶乘
}
while (n -- )
{
int a, b;
scanf("%d%d", &a, &b);
printf("%d\n", (LL)fact[a] * infact[b] % mod * infact[a - b] % mod);
}
return 0;
}
3. 求组合数 III
对于模不确定的时候,我们不能用预处理的方法,我们此时采用卢卡斯定理:
对于非负整数
a
,
b
a , b
a,b 和质数
p
p
p ,有
C
a
b
≡
C
a
m
o
d
p
b
m
o
d
p
⋅
C
⌊
a
/
p
⌋
⌊
b
/
p
⌋
(
m
o
d
p
)
C_{a}^{b} \equiv C_{a \bmod p}^{b \bmod p} \cdot C_{\lfloor a / p\rfloor}^{\lfloor b / p\rfloor}(\bmod p)
Cab≡Camodpbmodp⋅C⌊a/p⌋⌊b/p⌋(modp)
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
int qmi(int a, int b, int p)
{
LL res = 1;
while (b)
{
if (b & 1) res = (LL)res * a % p;
a = (LL)a * a % p;
b >>= 1;
}
return res;
}
int C(LL a, LL b, int p)
{
if (b > a) return 0;
LL res = 1;
for (int i = a; i > b; i -- ) res = (LL)res * i % p;
for (int i = 1; i <= a - b; i ++ ) res = (LL)res * qmi(i, p - 2, p) % p;
return res;
}
int lucas(LL a, LL b, int p)
{
if (a < p && b < p) return C(a, b, p);
return (LL)C(a % p, b % p, p) * lucas(a / p, b / p, p) % p;
}
int main()
{
int n;
scanf("%d", &n);
while (n -- )
{
LL a, b, p;
scanf("%lld%lld%lld", &a, &b, &p);
printf("%d\n", lucas(a, b, p));
}
return 0;
}
4. 满足条件的01序列
卡特兰数是组合数学
Q
Q
Q 中的一种著名数列,通常用如下通项式表示(为了不与组合数
C
C
C 冲突,本文用
f
f
f 表示卡特兰数):
f
(
n
)
=
C
2
n
n
n
+
1
f(n)=\frac{C_{2 n}^{n}}{n+1}
f(n)=n+1C2nn
AcWing 889. 满足条件的01序列
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 1e5 + 10, mod = 1e9 + 7;;
int qmi(int a, int b, int p)
{
LL res = 1;
while (b)
{
if (b & 1) res = (LL)res * a % p;
a = (LL)a * a % p;
b >>= 1;
}
return res;
}
int main()
{
int n;
scanf("%d", &n);
LL res = 1;
int a = 2 * n, b = n;
for (int i = a; i > b; i -- ) res = (LL)res * i % mod;
for (int i = 1; i <= b; i ++ ) res = (LL)res * qmi(i, mod - 2, mod) % mod;
res = (LL)res * qmi(n + 1, mod - 2, mod) % mod;
printf("%d\n", res);
return 0;
}