目录
高斯消元法
例题:
高斯消元步骤:
!!!枚举每一列col
- 找到绝对值最大的那一行
- 将该行换到最上面
- 将该行的第一个数变成1
- 将下面所有行的第col列的数消成0(通过原系数-原系数*上一个方程为什么要变成1的原因)
核心思想:
将多组方程构造为阶梯型(三角形):即如下图所示,那么就可以由下层依次推出上层的解
多种情况:(可以看上面的人话版本)
- 无解:若在最后化成的上三角形矩阵中,正对角线中某个元素为0,但其所在行的最后一列元素不为0时,此时矩阵无解
- 有无数解:若在最后化成的上三角形矩阵中,存在正对角线中某个元素为0,且其所在行的最后一列元素也为0时,此时矩阵有无穷组解、
- 有唯一解:若在最后化成的上三角形矩阵中,不存在正对角线中某个元素为0,此时矩阵有唯一解
代码实现:
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
const int N = 110;
/* C++浮点数存在误差,不能直接判断0,要判断是否小于一个很小的数,
如果小于这个很小的数,就认为是0,如小于1e-6*/
const double eps = 1e-6;
int n;
double a[N][N];
int gauss()
{
int c, r;// 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;
// 如果这一列中最大值已经是0了,直接continue进入下一列
if (fabs(a[t][c]) < eps) continue;
// ② 将这行换到最上面
// 从当前列c开始交换后面的值到第一行
for (int i = c; i < n + 1; i++) swap(a[t][i], a[r][i]);
// ③ 将该行第一个数变成1
// 将r行中c列后的每一个元素除上一个a[r][c](该行的第一个不为零的元素)
for (int i = n; i >= c; i--) a[r][i] /= a[r][c];
// ④ 用这一行将下面每一行的该列清0
for (int i = r + 1; i < n; i++)
// 如果该行的该列元素不是0才进行操作
if (fabs(a[i][c]) > eps)
for (int j = n; j >= c; j--)
a[i][j] -= a[r][j] * a[i][c];//删成0的操作
r++; // r表示消0后最后剩余的行数
}
// 无解和无穷多解的判断
if (r < n)
{
// 如果出现了等号左右一个为0一个非0,则说明无解
for (int i = r; i < n; i++)
if (fabs(a[i][n]) > eps)
return 2;
// 否则说明有无穷多解
return 1;
}
// 回溯计算每个值xi
for (int i = n - 1; i >= 0; i--)
for (int j = i + 1; j < n; j++)
a[i][n] -= a[j][n] * a[i][j];
// 有唯一解
return 0;
}
int main()
{
cin >> n;
// 存储线性方程组的增广矩阵
for (int i = 0; i < n; i++)
for (int j = 0; j < n + 1; j++)
cin >> a[i][j];
// 执行高斯消元
int t = gauss();
// 如果 t = 0,有唯一解
if (t == 0)
{ // %.2lf 保留两位小数
for (int i = 0; i < n; i++) printf("%.2lf\n", a[i][n]);
}
// 如果 t = 1,线性方程组存在无数解
else if (t == 1) puts("Infinite group solutions");
// 否则无解
else puts("No solution");
return 0;
}
组合数导航
组合数(一)(组合数简单性质)
组合数的性质:
解释:共有a个苹果,我从中选择b个苹果 等价于
在a个苹果中独立出来a-1个苹果和1个苹果
我从a-1个苹果中选择b个苹果(这b个苹果中不包括那个被独立处出来的那一个苹果)
+我从a-1个苹果中选择b-1个苹果(这b-1个苹果中包括了那个被独立出来那一个的苹果)
#include <iostream>
#include <algorithm>
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;
init();
cin >> n;
while (n--)
{
int a, b;
cin >> a >> b;
printf("%d\n", c[a][b]);
}
return 0;
}
组合数(二)(快速幂求逆元)
思路:
- 预处理阶乘
- 预处理逆元的阶乘
- 由公式求出组合数
图解: [AcWing] 886. 求组合数 II(C++实现)求组合数第二种题型模板题_cloud的博客-CSDN博客
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 100010, mod = 1e9 + 7;
int fact[N], infact[N];
// 快速幂 :求a的k次方模p的结果
int qmi(int a, int k, int p)
{
int res = 1;
while (k)
{
if (k & 1) res = (LL)res * a % p;
a = (LL)a * a % p;
k >>= 1;
}
return res;
}
int main()
{
fact[0] = infact[0] = 1;
for (int i = 1; i < N; i++)
{
// 求阶乘
fact[i] = (LL)fact[i - 1] * i % mod;
// 求逆元的阶乘
infact[i] = (LL)infact[i - 1] * qmi(i, mod - 2, mod) % mod;
}
int n;
scanf("%d", &n);
while (n--)
{
int a, b;
scanf("%d%d", &a, &b);
// 由阶乘和逆元通过公式算出c[a][b]的值
printf("%d\n", (LL)fact[a] * infact[b] % mod * infact[a - b] % mod);
}
return 0;
}
组合数(三)(卢卡斯定理)
[AcWing] 887. 求组合数 III(C++实现)求组合数第三种题型(卢卡斯定理)模板题_cloud的博客-CSDN博客
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
// 快速幂求a的k次方模p
int qmi(int a, int k, int p)
{
int res = 1;
while (k)
{
if (k & 1) res = (LL)res * a % p;
a = (LL)a * a % p;
k >>= 1;
}
return res;
}
//由定义计算组合数
int C(int a, int b, int p)
{
if (b > a) return 0; // 如果b > a,组合数为0
int res = 1;
// 由定义计算组合数
for (int i = 1, j = a; i <= b; i ++, j -- )
{
// 乘j
res = (LL)res * j % p;
// 除i,注意:除i等同于乘i的逆元
res = (LL)res * qmi(i, p - 2, p) % p;
}
return res;
}
// 卢卡斯定理
int lucas(LL a, LL b, int p)
{
// 如果a和b都小于p,从定义来计算组合数C
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;
cin >> n;
while (n -- )
{
LL a, b;
int p;
cin >> a >> b >> p;
// 卢卡斯定理求组合数
cout << lucas(a, b, p) << endl;
}
return 0;
}
组合数(四)(高精度)
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
const int N = 5010;
int primes[N], cnt;
int sum[N];
bool st[N];
// ① 线性筛,筛质数
void get_primes(int n)
{
for (int i = 2; i <= n; i++)
{
if (!st[i]) primes[cnt++] = i;
for (int j = 0; primes[j] <= n / i; j++)
{
st[primes[j] * i] = true;
if (i % primes[j] == 0) break;
}
}
}
// ② 计算n的阶乘中素数p的次数
int get(int n, int p)
{
int res = 0;
while (n)
{
res += n / p;
n /= p;
}
return res;
}
// ③ 高精度乘法
vector<int> mul(vector<int> a, int b)
{
vector<int> c;
int t = 0;
for (int i = 0; i < a.size(); i++)
{
t += a[i] * b;
c.push_back(t % 10);
t /= 10;
}
while (t)
{
c.push_back(t % 10);
t /= 10;
}
return c;
}
int main()
{
int a, b;
cin >> a >> b;
// ① 分解质因数
get_primes(a);
// ② 计算阶乘中每个素数的次数
for (int i = 0; i < cnt; i++)
{
int p = primes[i];
// 用分母的质因子减去分子的质因子就是组合数C的质数的次数
sum[i] = get(a, p) - get(a - b, p) - get(b, p);
}
// ③ 高精度乘法
vector<int> res;
res.push_back(1);
for (int i = 0; i < cnt; i++)//素数的个数
for (int j = 0; j < sum[i]; j++)//组合数C的质数的次数
res = mul(res, primes[i]);
// 输出该值
for (int i = res.size() - 1; i >= 0; i--) printf("%d", res[i]);
puts("");
return 0;
}
组合数(五)(卡特兰数)
原理:[AcWing] 889. 满足条件的01序列(C++实现)求组合数第五种题型(卡特兰数)模板题_cloud的博客-CSDN博客
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 100010, mod = 1e9 + 7;
// 快速幂
int qmi(int a, int k, int p)
{
int res = 1;
while (k)
{
if (k & 1) res = (LL)res * a % p;
a = (LL)a * a % p;
k >>= 1;
}
return res;
}
int main()
{
int n;
cin >> n;
int a = n * 2, b = n;
int res = 1;
for (int i = a; i > a - 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;
cout << res << endl;
return 0;
}