Rui_R
2020-10-24 08:33:41
狠题。
题意:在高斯消元的基础上,判断是正常的还是“无解”还是“有无数解”。
简述一下高斯消元的思想:让第
一般的高斯消元每次会选择该项系数绝对值最大的来对应第
那么我们来研究,没有正常的解,究竟是“无解”还是“有无数解”。
一个naive的想法是,在完成消元后枚举每一项,如果发现系数是0则讨论等式右边:如果为0,则有无数解;如果不为0,则无解。其中无解优先级更高,即如果一个方程有一项无解且有一项有无数解,那么判定无解。
交上去喜提90。
存在漏洞:以 hehe_zhou 大佬的hack数据为例:
2
0 2 3
0 0 0
答案显然是0,但是我们的程序会说这是-1。
我们发现,我们可以根据第一个式子来求出第二元,但是程序会用它来算第一元,并且以后算第二元的时候不会再考虑该式,因为我们认为它已经对应第一元了!
也就是说,我们的答案受到消元顺序影响。
题解里部分选手当场召唤玄学势力,把消元顺序随机乱改,大概率能出正解。
这里提供一种新的做法。
注意到,我们的症结在于把还有用的式子放到了用不上它的地方,并且以后也不来看它是否可用。
那么,我们就来看它是否可用。
原本的高斯消元中,我们认为只有
但这里,我们有可能会在系数为0之后继续做下去。这就是受到消元顺序影响的原因。
那么,可用的就不仅仅是之后的式子,还有之前系数为0的式子。
于是解决了。
#include <cstdio>
const int maxn=55;const double eps=1e-6;
int n;double a[maxn][maxn];
template<typename T> inline void swap(T &a,T &b){
T temp=a;a=b;b=temp;
}
template<typename T> inline T abs(T val){
return val>0?val:-val;
}
void Gauss(){//高斯消元
for(int i=1;i<=n;i++){
int maxx=i;
for(int j=1;j<=n;j++){//把枚举范围从i->n改成了1->n
if(abs(a[j][j])>eps&&j<i) continue;//若在i前,且的确已对应一项系数,不可用
if(abs(a[j][i])>abs(a[maxx][i])){
maxx=j;
}
}
for(int j=1;j<=n+1;j++) swap(a[maxx][j],a[i][j]);
if(abs(a[i][i])<=eps) continue;
for(int j=1;j<=n;j++){
if(i==j) continue;
double delta=a[j][i]/a[i][i];
for(int k=i;k<=n+1;k++){
a[j][k]-=delta*a[i][k];
}
}
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
for(int j=1;j<=n+1;j++){
scanf("%lf",&a[i][j]);
}
}
Gauss();int key=1;
for(int i=1;i<=n;i++){
// for(int j=1;j<=n+1;j++) printf("%.2lf ",a[i][j]);
// printf("\n");
if(abs(a[i][i])<=eps){
if(abs(a[i][n+1])>eps) key=-1;//无解优先级更高
else if(key!=-1) key=0;
}
}
if(key!=1) printf("%d\n",key);
else for(int i=1;i<=n;i++) printf("x%d=%.2lf\n",i,abs(a[i][n+1]/a[i][i])<=eps?0:a[i][n+1]/a[i][i]);//防止出现-0.00这种令人郁闷的情况,虽然对于本题数据无所谓
return 0;
}
如果hack掉了,请告知这个白痴。