http://hihocoder.com/contest/hiho56/problem/1
高斯消元就是用多元一次方程求解
小Ho:<吧唧><吧唧><吧唧>
小Hi:小Ho,你还吃呢。想好了么?
小Ho:肿抢着呢(正想着呢)…<吞咽>…我记得这个问题上课有提到过,应该是一元一次方程组吧。
我们把每一件商品的价格看作是x[1]…x[n],第i个组合中第j件商品数量记为a[i][j],其价格记作y[i],则可以列出方程式:
我们可以对方程组进行3种操作而不改变方程组的解集:
-
交换两行。
-
把第i行乘以一个非0系数k。即对于j = 1…n, 令a[i][j] = ka[i][j], y[i]=ky[i]
-
把第p行乘以一个非0系数k之后加在第i行上。即对于j=1…n, 令a[i][j] = a[i][j]+ka[p][j],y[i]=y[i]+kp[i]
以上三个操作叫做初等行变换。
我们可以使用它们,对这个方程组中的a[i][j]进行加减乘除变换,举个例子:
我们可以通过 式子(1) - 式子(2) * (a[1][1] / a[2][1]),将第1行第1列的a[1][1]变换为0。
对整个方程组进行多次变换之后,可以使得a[i][j]满足:
伪代码:
// 处理出上三角矩阵
For i = 1..N
Flag ← False
For j = i..M // 从第i行开始,找到第i列不等于0的行j
If a[j][i] != 0 Then
Swap(j, i) // 交换第i行和第j行
Flag ← True
Break
End If
End For
// 若无法找到,则存在多个解
If (not Flag) Then
manySolutionsFlag ← True // 出现了秩小于N的情况
continue;
End If
// 消除第i+1行到第M行的第i列
For j = i+1 .. M
a[j][] ← a[j][] - a[i][] * (a[j][i] / a[i][i])
b[j] ← b[j] - b[i] * (a[j][i] / a[i][i])
End For
End For
// 检查是否无解,即存在 0 = x 的情况
For i = 1..M
If (第i行系数均为0 and (b[i] != 0)) Then
return "No solutions"
End If
End For
// 判定多解
If (manySolutionsFlag) Then
return "Many solutions"
End If
// 此时存在唯一解
// 由于每一行都比前一行少一个系数,所以在M行中只有前N行有系数
// 解析来从第N行开始处理每一行的解
For i = N .. 1
// 利用已经计算出的结果,将第i行中第i+1列至第N列的系数消除
For j = i + 1 .. N
b[i] ← b[i] - a[i][j] * value[j]
a[i][j] ← 0
End For
value[i] ← b[i] / a[i][i]
End For
AC代码:
注意,用double的大于0的时候,要去一个eps近似值,
还有最后输出的时候要转成int(x+0.5),原因我还不知道,如果有知道的,可以在评论区指出来,感谢^- ^
#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int Mod = 1e9 + 7;
const int maxn = 1e3 + 5;
const double eps = 0.0000001;
const int INF = 0x3f3f3f3f;
int n, m;
double a[maxn][maxn], x[maxn];
bool manySolutionFlag = false, noSolution = false;
void Swap(int i, int j) {
for (int k = 1; k <= n + 1; k ++)
swap(a[i][k], a[j][k]);
}
bool Check(int i) {
bool vis = false;
for (int j = 1; j <= n; j ++) {
if(fabs(a[i][j]) >= eps) vis = true;
}
if(!vis && fabs(a[i][n + 1]) >= eps) return false;
return true;
}
void GS() {
for (int i = 1; i <= n; i ++) {
bool flag = false;
for (int j = i; j <= m; j ++) {
if(a[j][i] != 0) {
Swap(j, i);
flag = true;
break;
}
}
if(!flag) {
manySolutionFlag = true;
}
for (int j = i + 1; j <= m; j ++)
for (int k = n + 1; k >= i; k --)
a[j][k] = a[j][k] * 1. - a[i][k] * (a[j][i] * 1./a[i][i] * 1.) * 1.;
}
for (int i = 1; i <= m; i ++) {
if(!Check(i)) {
noSolution = true;
return ;
}
}
for (int i = n; i >= 1; i --) {
for (int j = i + 1; j <= n; j ++) {
a[i][n + 1] = a[i][n + 1] - a[i][j] * x[j];
a[i][j] = 0;
}
x[i] = a[i][n + 1] * 1./a[i][i] * 1.;
}
}
int main()
{
cin >> n >> m;
for (int i = 1; i <= m; i ++)
for (int j = 1; j <= n + 1; j ++)
cin >> a[i][j];
GS();
if(noSolution) cout << "No solutions" << endl;
else if(manySolutionFlag) cout << "Many solutions" << endl;
else {
for (int i = 1; i <= n; i ++)
cout << (int)(x[i] +0.5) << endl;
}
return 0;
}