题解 P2455 【[SDOI2006]线性方程组】
看到各位大佬的高斯消元都那么厉害,而我只会高斯模板,我很慌啊
但还是交了一个高斯消元模板,然后只有80
各种改,最高改到90,但更多是10,20
经过我一番思考 (刷光谈论区)后,我意识到一个问题:
高斯消元的顺序可能会影响到最终的答案,主要是0与-1的情况
(当然,你只要像各位大佬一样,高斯消元打的够好,或者优化的好,完全可以无视它)
那么,怎么办?
既然高斯消元的顺序会影响最终结果,那么我们就把乱改的思路贯彻到底
——采用随机化算法
既然我把顺序打乱了,那么各种卡顺序的数据也就GG了
上面是随机化思想优化的引入,下面稍微讲一下这道题(反正前人之述备矣)
首先,你要会高斯消元,如果你不懂,请转P3389
其次,为防止各种hack数据,请随机打乱矩阵的行
最后,你高斯消元完了之后,你去计算每一行的a[i][n+1]/a[i][i] (常数/系数)
若a[i][i]不等于0,直接输出商
否则
{
如果a[i][n+1]==0 那么就是 0
否则 就是 -1
}
以上是小学数学书上的解释,不懂回去重造
上代码
(已做防抄袭处理,不要和,自己写)
#include <bits/stdc++.h>
#define random(a,b) ((a)+rand()%((b)-(a)+1))
using namespace std;
int n,b[111],fg;
double a[111][111],f[111];
int main()
{
srand(time(NULL));//随机化预处理
cin>>n;
// for(int i=1;i<=n;i++) b[i]=1;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n+1;j++)
{
scanf("%lf",&a[i][j]); //输入
}
}
for(int i=1;i<=n;i++) //进行随机化交换任意两行
{
for(int j=i+1;j<=n;j++)
{
int x=random(1,1000);
// printf("%d ",x);
if(x&1)
{
for(int k=1;k<=n+1;k++)
{
swap(a[i][k],a[j][k]);
}
}
}
}
/*
puts("");
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n+1;j++)
{
printf("%lf ",a[i][j]);
}
puts("");
}
*/
for(int i=1;i<=n;i++) //高斯消元
{
int mx=i;
for(int j=i+1;j<=n;j++) //约旦算法,查abs最大的先做(由于已经过随机化算法,这个做法也不易被卡)
{
if(fabs(a[j][i])>fabs(a[mx][i]))
{
mx=j;
}
}
// printf("#%d %lf %d\n",mx,a[mx][i],a[mx][i]==0);
/*
if(a[mx][i]==0)
{
// printf("@%d %lf\n",mx,a[mx][n+1]);
if(!a[mx][n+1])
{
puts("0");
return 0;
// puts("0");
// return 0;
}
else
{
fg=1;
continue;
}
}
*/
if(!a[mx][i]) continue;//a[mx][i]==0就continue掉,不然除0会爆nan的!!!
for(int j=1;j<=n+1;j++)
{
swap(a[i][j],a[mx][j]);
}
for(int j=1;j<=n;j++)
{
if(j!=i)
{
double t=a[j][i]/a[i][i]; //加减消元
for(int k=1;k<=n+1;k++)
{
a[j][k]-=a[i][k]*t;
}
}
}
}
/*
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n+1;j++)
{
printf("%lf ",a[i][j]);
}
puts("");
}
*/
for(int i=1;i<=n;i++) //用f[i]存结果
{
// if(b[i]>0)
if(a[i][i]==0) //判断有无解,只要一个无解,那么就算其他有无数解,也算无解
{
if(a[i][n+1])
{
puts("-1");
return 0; //所以无解优先
}
else fg=1;
continue;
}
// printf("%lf %lf\n",a[i][n+1],a[i][i]);
f[i]=a[i][n+1]/a[i][i];
if(!f[i]) f[i]=0.00; //防止-0.00这种狗屎情况
// else printf("%d\n",b[i]);
}
if(fg)
{
puts("0");
return 0;
}
for(int i=1;i<=n;i++)
{
printf("x%d=%.2f\n",i,f[i]);//输出
}
return 0;
}
如果你觉得他太长,还有短的
压行前130多,压行后30多,无语
#include <bits/stdc++.h>
#define random(a,b) ((a)+rand()%((b)-(a)+1))
using namespace std;
int n,b[111],fg;
double a[111][111],f【111】;
int main() {
srand(time(NULL)); #随机化预处理
cin>>n;
for(int i=1;i<=n;i++)
for(int j=1;j<=n+1;j++)
scanf("%lf",&a[i][j]); //输入
for(int i=1;i<=n;i++) \\进行随机化交换任意两行
for(int j=i+1;j<=n;j++){
int x=random(1,1000);
if(x&1) for(int k=1;k<=n+1;k++) swap(a[i][k],a[j][k]);
}
for(int i=1;i<=n;i++){ 、、高斯消元
int mx=i;
for(int j=i+1;j<=n;j++) if(fabs(a[j][i])>fabs(a[mx][i])) mx=j;//约旦算法,查abs最大的先做(由于已经过随机化算法,这个做法也不易被卡)
if(!a[mx][i]) continue; !!!a[mx][i]==0就continue掉,不然除0会爆nan的!!!
for(int j=1;j<=n+1;j++) swap(a[i][j],a[mx][j]);
for(int j=1;j<=n;j++) if(j!=i){
double t=a[j][i]/a[i][i]; //加减消元
for(int k=1;k<=n+1;k++) a[j][k]-=a[i][k]*t;
}
}
for(int i=1;i<=n;i++){ //用f[i]存结果
if(a[i][i]==0){ //判断有无解,只要一个无解,那么就算其他有无数解,也算无解
if(a[i][n+1]) return puts("-1"),0; !!!所以无解优先^__^
else fg=1;
continue;
}
f[i]=a[i][n+1]/a[i][i];
if(!f[i]) f[i]=0.00; //防止-0.00这种狗屎情况
}
if(fg) return puts("0"),0;
for(int i=1;i<=n;i++) printf("x%d=%.2f\n",i,f[i]);//输出
}《《————你猜猜这大括号中文英文
总之,随机化算法可以避免你常规算法的各种问题,是一种不错的算法
(就是还得看脸), 其他就是正常的高斯消元,对0,与-1的情况多加留意就是,此外还有些细节,好好处理,耐心点就好 调料一个晚上
最后,祝大家 AK NOIP 2019