高斯消元
高斯消元可以用来解方程,可以在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的系数
上面的值输入后,方程是这样
初等行列变换:
把某一行乘一个非0的数
交换某2行
把某行的若干倍加到另一行去
之后变为上三角形式
上三角形式有三种:①完美阶梯型——有唯一解,②非完美阶梯型,左边没有未知数,右边的系数是非0的,即0=非0,此时无解。③出现0=0,有无穷多组解。
算法步骤:这里的目的是要消成上三角形式
枚举每一列C。
找到绝对值最大的一行。
将该行换到最上面去。
将该行第一个数变成1.这里等式俩边同时除2
将下面所有行的当前列全部消成0,这里用第二行减去第一行,第二行就变为0了
第三行,把第一行加到第三行即可。
把当前第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-5000内的素数筛出来
求每个质数的次数,用上图最后一个公式求
用高精度乘法,把所有质因子乘到一块去。
#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;
}