算法基础-数学知识-高斯消元、求组合数

高斯消元

  1. 找到当前列绝对值最大的数 所在的行
  2. 将改行的该列的系数变成1,其他列也要跟着变
  3. 将这行和最上面未处理的那行交换(不是第一行)
  4. 最上面那行的以下的所有行的该列消元
  5. 判断是否存在解 123三种情况
  6. 若有唯一解,则从最下面开始消元

883. 高斯消元解线性方程组

#include <iostream>
#include <algorithm>
#include <cmath>

using namespace std;
const int N = 110;
const double eps = 1e-6;//eps是epsilon,表示很小的值
double a[N][N];
int n;
int gauss()
{
    int c, r;
    for (c = 0, r = 0; c < n; ++ c) // ++ r不能放在这一行,因为不是每一个a[t][c]都合法
    {
        int t = r;
        for (int i = r; i < n; ++ i)
            if (fabs(a[t][c]) < fabs(a[i][c])) 
                t = i;
        if (fabs(a[t][c]) < eps) continue;//说明就没有未知数Xc,他的系数最大就是0
        //交换
        for (int i = c; i <= n; ++ i) swap(a[t][i], a[r][i]);
        //将这一行的这一列变成1
        for (int i = n; i >= c; -- i) a[r][i] /= a[r][c];//后面的状态需要第一个状态作为基准,因此从后面推

        //将下面的行对应的这一列消成0
        for (int i = r + 1; i < n; ++ i)
        {
            if (fabs(a[i][c]) > eps)
                for (int j = n; j >= c; -- j)
                    a[i][j] -= a[r][j] * a[i][c];
        }

        r ++;
    }
    //c此时=n
    if (r < c)// 说明剩下方程的个数是小于 n 的,说明不是唯一解,判断是无解还是无穷多解
    {// 因为已经是阶梯型,所以 r ~ n-1 的值应该都为 0
        for (int i = r; i < n; i ++ )// 
            if (fabs(a[i][n]) > eps)// a[i][n] 代表 b_i ,即 左边=0,右边=b_i,0 != b_i, 所以无解。
                return 2;
        return 1;// 否则, 0 = 0,就是r ~ n-1的方程都是多余方程
    }
    for (int i = r; i >= 0; -- i)//r此时=n-1
        for (int j = i + 1; j < n; ++ j)
            a[i][n] -= a[i][j] * a[j][n];
            
    return 0;
}
void solve()
{
    cin >> n;
    for (int i = 0; i < n; ++ i)
        for (int j = 0; j <= n; ++ j)
            cin >> a[i][j];   
    int t = gauss();
    if (t == 1) puts("Infinite group solutions");
    else if (t == 2) puts("No solution");
    else  
    {
        for (int i = 0; i < n; i ++ ) printf("%.2lf\n", a[i][n]);
    }
}
int32_t main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    int T = 1;
    //cin >> T;
    while (T --) solve();
    return 0;
}

组合数

在这里插入图片描述

  1. 递推就是Cba = Cba-1 + Cb-1a-1
  2. 预处理就是qmi (logN复杂度)结合模运算里面的逆元C^b^~a~ = a! / ((a-b)!*b!),提前将所有阶乘的逆元和阶乘的值算出来
  3. lucas定理证明我看了懵懵懂懂,记住结论就行

AcWing 885. 求组合数 I

#include <iostream>
#include <algorithm>
#include <cmath>

using namespace std;
const int N = 2e3 + 10, 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 == 0) c[i][j] = 1;//i == j的情况可以通过前面处理好的递推出来
            else c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod;
}
void solve()
{
    int n;
    cin >> n;
    init();
    while (n -- )
    {
        int a, b;
        cin >> a >> b;
        cout << c[a][b] << endl;
    }
}
int32_t main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    int T = 1;
    //cin >> T;
    while (T --) solve();
    return 0;
}

AcWing 886. 求组合数 II

  1. 运用组合数的定义,即最基本的运算公式。
  2. 基本运算公式中含有除法,我们想把除法变成乘法。
  3. 前面说了除法变乘法,本题正好有取模预算,取模运算下除法变乘法可以用欧拉函数扩展。
  4. 模又是质数,可以用费马定理,费马定理可以用快速幂将N优化为logN复杂度
#include <iostream>
#include <algorithm>
#include <cmath>

using namespace std;
typedef long long LL;
const int N = 1e5 + 10, mod = 1e9 + 7;
int fact[N];//fact[i]表示i的阶乘
int infact[N];//infact[i]表示i阶乘的逆元,可以由infact[i - 1] * i的逆元获得
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;
}
void solve()
{
    int n;
    cin >> n;
    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;
    }
    while (n -- )
    {
        int a, b;
        cin >> a >> b;
        printf("%d\n", (LL)fact[a] * infact[b] % mod * infact[a - b] % mod);
    }
}
int32_t main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    int T = 1;
    //cin >> T;
    while (T --) solve();
    return 0;
}

AcWing 887. 求组合数 III

在这里插入图片描述

#include <iostream>
#include <algorithm>
#include <cmath>

using namespace std;
typedef long long LL;

int qmi(int a, int k, int p)
{
    LL res = 1;
    while (k)
    {
        if (k & 1) res = res * a % p;
        a = (LL)a * a % p;
        k >>= 1;
    }
    return res;
}
int C(LL a, LL b, int p)//好坑,a和b可能是LL
{
    if (a < b) return 0;
    if (b > a - b) b = a - b; //Ca b = Ca a - b,可以自己举个例子
    int x = 1, y = 1;
    for (int i = 0; i < b; ++ i)//就是C^b^~a~ = a! / ((a-b)!*b!)
    {
        x = (LL)x * (a - i) % p;
        y = (LL)y * (i + 1) % p;
    }
    return (LL)x * qmi(y, p - 2, p) % p;//p一定为质数
}

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;
}

void solve()
{
    int n;
    cin >> n;
    LL a, b;
    int p;
    while (n -- )
    {
        cin >> a >> b >> p;//好坑,a和b可能是LL
        cout << lucas(a, b, p) << endl;
    }    
}
int32_t main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    int T = 1;
    //cin >> T;
    while (T --) solve();
    return 0;
}

AcWing 888. 求组合数 IV

在这里插入图片描述

#include <iostream>
#include <algorithm>
#include <cmath>

using namespace std;
const int N = 5e3;
int primes[N], cnt;
int sum[N];
bool st[N];
void init(int a)//初始化获得1-a之间所有的质数,数论里面学过 a一定可以分解成质因数的乘积
{
    for (int i = 2; i <= a; ++ i)
    {
        if (!st[i]) primes[cnt++] = i;
        for (int j = 0; primes[j] <= a / i; ++ j)
        {
            st[i * primes[j]] = true;
            if (i % primes[j] == 0) break;
        }       
    }
}

int get(int x, int p)//获得每个质因数的数量
{
    int res = 0;
    while (x)
    {
        res += x / p;
        x /= p;
    }
    return res;
}

vector<int> mul(vector<int> a, int b) //高精度乘法
{
    int t = 0;
    vector<int> C;
    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;
}

void solve()
{
    int a, b;
    cin >> a >> b;
    init(a);   
    for (int i = 0; i < cnt; ++ i)//计算结果中的所有质因数的数量
        sum[i] = get(a, primes[i]) - get(a - b, primes[i]) - get(b, primes[i]);
    

    vector<int> res;
    res.push_back(1);
    for (int i = 0; i < cnt; ++ i)
        for (int j = 1; j <= sum[i]; ++ j)
            res = mul(res, primes[i]);
            
    for (int i = res.size() - 1; i >= 0; -- i) cout << res[i];
}
int32_t main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    int T = 1;
    //cin >> T;
    while (T --) solve();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要使用Python进行高斯消元解线性方程组的解,你可以按照以下步骤进行操作: 1. 创建一个表示线性方程组的矩阵。假设你有n个未知数和n个方程,你可以创建一个n x (n+1)的增广矩阵,其中最后一列是方程组的常数项。 2. 实现高斯消元算法来将增广矩阵转化为上三角形矩阵。高斯消元算法包括以下步骤: - 遍历每一行,找到主元素(即该行第一个非零元素)。 - 如果主元素不在当前行,则交换行。 - 使用主元素将当前行下面的所有行进行消元,使得当前列下面的元素变为零。 3. 实现回代算法解上三角形矩阵的解。回代算法包括以下步骤: - 从最后一行开始,计算未知数的值。 - 将已知未知数的值代入到上面的行中,逐步计算出其他未知数的值。 下面是一个示例代码,用于解线性方程组的解: ```python import numpy as np def gaussian_elimination(A): n = len(A) for i in range(n): max_row = i for j in range(i+1, n): if abs(A[j][i]) > abs(A[max_row][i]): max_row = j A[i], A[max_row] = A[max_row], A[i] for j in range(i+1, n): ratio = A[j][i] / A[i][i] for k in range(i, n+1): A[j][k] -= ratio * A[i][k] return A def back_substitution(A): n = len(A) x = np.zeros(n) for i in range(n-1, -1, -1): x[i] = A[i][n] / A[i][i] for j in range(i-1, -1, -1): A[j][n] -= A[j][i] * x[i] return x # 示例 A = np.array([[2, 1, -1, 8], [-3, -1, 2, -11], [-2, 1, 2, -3]]) A = gaussian_elimination(A) x = back_substitution(A) print("方程组的解为:", x) ``` 这个示例中,我们使用numpy库创建一个增广矩阵A,然后分别调用高斯消元算法和回代算法解线性方程组的解。你可以根据你的具体问题修改矩阵A的数值来解不同的线性方程组。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值