第四章——数学知识3

高斯消元

高斯消元可以用来解方程,可以在n三次方的时间复杂度内,求多元线性方程组。

答案只有三种情况,无解,无穷多组解,唯一解

输入一个包含 n 个方程 n 个未知数的线性方程组。

方程组中的系数为实数。

求解这个方程组。

下图为一个包含 m 个方程 n 个未知数的线性方程组示例:

输入格式

第一行包含整数 n。

接下来 n 行,每行包含 n+1 个实数,表示一个方程的 n 个系数以及等号右侧的常数。

输出格式

如果给定线性方程组存在唯一解,则输出共 n 行,其中第 i 行输出第 i 个未知数的解,结果保留两位小数。

如果给定线性方程组存在无数解,则输出 Infinite group solutions。

如果给定线性方程组无解,则输出 No solution。

数据范围

1≤n≤100,

所有输入系数以及常数均保留两位小数,绝对值均不超过 100。

输入样例:

3

1.00 2.00 -1.00 -6.00

2.00 1.00 -3.00 -9.00

-1.00 -1.00 2.00 7.00

输出样例:

1.00

-2.00

3.00

输入的时候,输入的是x1-xn的系数

上面的值输入后,方程是这样

初等行列变换:

  1. 把某一行乘一个非0的数

  1. 交换某2行

  1. 把某行的若干倍加到另一行去

之后变为上三角形式

上三角形式有三种:①完美阶梯型——有唯一解,②非完美阶梯型,左边没有未知数,右边的系数是非0的,即0=非0,此时无解。③出现0=0,有无穷多组解。

算法步骤:这里的目的是要消成上三角形式

  1. 枚举每一列C。

  1. 找到绝对值最大的一行。

  1. 将该行换到最上面去。

  1. 将该行第一个数变成1.这里等式俩边同时除2

  1. 将下面所有行的当前列全部消成0,这里用第二行减去第一行,第二行就变为0了

第三行,把第一行加到第三行即可。

  1. 把当前第2列的数变成1.

接下来先把最后一个方程第一个不为0的数变成1

最终变为这种形式

接下来直接解即可

#include<iostream>
#include<cmath>
using namespace std;
                                                 高斯消元法
const int N = 110;
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++)//先从第0行,第0列开始枚举,枚举到最后一列为止
    {
        int t = r;
        for(int i=r;i<n;i++)
            if(fabs(a[i][c])>fabs(a[r][c]))//先找到这一列,绝对值最大的一行
                //如果当前这一行第C列的绝对值大于当前所存的绝对值,就换到这一行
                t=i;
        if (fabs(a[t][c])<eps)//如果当前这一列是0
            continue;
        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];
        //把下面所有行的第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++;
    }
    if (r < n)//剩下的方程的个数小于n,不是唯一解
    {
        for (int i = r; i < n; i++)
            if (fabs(a[i][n] > eps))
                return 2;
        return 1;
    }
    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();//高斯消元
    //0 表示唯一解 1表示无穷多组解 2表示无解
    if (t == 0)
    {
        for (int i = 0; i < n; i++)
            printf("%.2lf\n", a[i][n]);
    }
    else if (t == 1)
    {
        puts("Infinite group solutions");
    }
    else
    {
        puts("No solution");
    }
    return 0;
}

求组合数1

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)//如果j为0,c[i][j]为1
                c[i][j] = 1;
            else c[i][j] = (c[i - 1][j] + c[i - 1][j - 1])%mod;//由于答案会很大,所以要给取模
}
int main()
{
    init();
    int n;
    cin >> n;
    while (n--)
    {
        int a, b;
        scanf("%d %d", &a, &b);
        printf("%d\n", c[a][b]);
    }
    return 0;
}

求组合数2

以下题解来自这里

const int N = 100010,mod=1e9+7;
typedef long long LL;
int fact[N], infact[N];
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);
        printf("%d\n", (LL)fact[a] * infact[b] % mod*infact[a-b]%mod);
    }
    return 0;
}

求组合数3

最重要的是这个,上面都是证明

typedef long long LL;
int p;
int qmi(int a, int k)
{
    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 res = 1;
    for (int i = 1, j = a; i <= b; i++,j--)
    {
        res = (LL)res * j % p;
        res = (LL)res * qmi(i, p - 2) % p;
    }
    return res;
}
int lucas(LL a, LL b)
{
    if (a < p && b < p) return C(a, b);
    return (LL)C(a % p, b % p) * lucas(a / p, b / p) % p;
}
int main()
{
    int n;
    cin >> n;
    while (n--)
    {
        LL a, b;
        cin >> a >> b >> p;
        cout << lucas(a, b) << endl;
    }
    return 0;
}

求组合数4

第一步分解质因数,第二部高精度乘法计算

  1. 筛素数,把1-5000内的素数筛出来

  1. 求每个质数的次数,用上图最后一个公式求

  1. 用高精度乘法,把所有质因子乘到一块去。

#include<vector>
using namespace std;
const int N = 5010;
int primes[N], cnt;
bool st[N];
int sum[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;
        }
    }
}
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);//预处理出来1-a中的所有质数
    //接下来求每一个质数的次数
    for (int i = 0; i < cnt; i++)
    {
        int p = primes[i];
        sum[i] = get(a,p) - get(b,p) - get(a - b,p);
    }
    vector<int> res;
    res.push_back(1);
    for (int i = 0; i < cnt; i++)
    {
        for (int j = 0; j < sum[i]; j++)
        {
            res = mul(res, primes[i]);  
        }
    }
    for (int i = res.size() - 1; i >= 0; i--)
        printf("%d", res[i]);
    puts("");
    return 0;
}

容斥原理

满足条件的01序列

一共五种方案

我们转化成从原点走路径的问题,当有6个0和6个1,需要计算从0,0走到6,6共有多少种方案,上述题目求的是从0,0到3,3的距离

我们规定0代表向右走一格,1向上走一格。

若数字是100110001110,代表先网上走一格,再往右走俩格……,即每种排列都对应一条路径。

题目要求任意前缀0的个数要大于1的个数,即在坐标图中任意时刻x≥y,即任意时刻,都应该在绿颜色的边下面或在绿边。

即从0,0走到n,n所有不经过红颜色这条边的个数

从0,0到6,6共有C12 6种走法,再减去经过红颜色这条边的数。

这里黄边,圈出来的部分经过了红边,我们利用红边做轴对称即可得到正确的路径

6,6关于红颜色这条边做轴对称对应的坐标是5,7,即从0,0到6,6的所有路径中,减去0,0到5,7的所有路径,即可得到正确答案。

const int mod = 1e9 + 7;
typedef long long LL;
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 = 2 * n, 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

头发没有代码多

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值