例1:[SDOI2006]线性方程组
传送门
例2:[JSOI2008]球形空间产生器
传送门
例3:Barracuda
传送门
例4
传送门
Solution
例1:
看似是一个高斯消元,但普通的程序交上去会被卡成 80 80 80分 W A WA WA。
我们看下面的一组hack数据:
2
0 2 3
0 0 0
化简 2 y + 3 = 0 , 0 = 0 2y+3=0,0=0 2y+3=0,0=0
易得该方程组有无数解,但是我们的程序会输出 − 1 -1 −1。
究其原因,我们在考虑第一个方程时有 f [ 1 ] [ 1 ] = 0 , f [ 1 ] [ 3 ] ≠ 0 f[1][1]=0,f[1][3]≠0 f[1][1]=0,f[1][3]=0,程序直接判断无解退出。
所以我们在排序时,我们应该将第一关键字大的排前面,第二关键字小的排前面,这样就可以避免这种情况的发生。
一些细节处理见代码:
#include<bits/stdc++.h>
#define eps 1e-6
using namespace std;
double a[105][105];
int flag1,flag2;int n;
double check(double x){
if(fabs(x)<=eps) return 0;
return x;
}
bool cmp(int k,int x,int y){
if(fabs(fabs(a[x][k])-fabs(a[y][k]))>eps) return fabs(a[x][k])>fabs(a[y][k]);
for(int i=k+1;i<=n;i++){
if(fabs(fabs(a[x][i])-fabs(a[y][i]))>eps) return fabs(a[x][i])<fabs(a[y][i]);
}
}
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]);
}
}
for(int i=1;i<=n;i++){
int maxn=i;
for(int j=i+1;j<=n;j++){
if(cmp(i,j,maxn)){
maxn=j;
}
}
for(int j=1;j<=n+1;j++){
swap(a[i][j],a[maxn][j]);
}
if(!check(a[i][i])){
continue;
}
for(int j=1;j<=n;j++){
if(j==i) continue;
double tmp=a[j][i]/a[i][i];
for(int k=i+1;k<=n+1;k++){
a[j][k]-=a[i][k]*tmp;
}
}
}
for(int i=1;i<=n;i++){
if(!check(a[i][i])&&check(a[i][n+1])){
cout<<"-1\n";
return 0;
}
}
for(int i=1;i<=n;i++){
if(!check(a[i][i])&&!check(a[i][n+1])){
cout<<"0\n";
return 0;
}
}
for(int i=1;i<=n;i++){
printf("x%d=%.2lf\n",i,check(a[i][n+1]/a[i][i]));
}
}
例2
设中心点坐标为 ( x 1 , x 2 , . . . , x n ) (x_1,x_2,...,x_n) (x1,x2,...,xn),依照题意列出 n + 1 n+1 n+1个方程,上下两方程相减得出 n n n个方程,有 n n n个未知数,高斯消元求解即可。
#include<bits/stdc++.h>
using namespace std;
int n;
double p[15][15],a[15][15];
int main(){
scanf("%d",&n);
for(int i=1;i<=n+1;i++){
for(int j=1;j<=n;j++){
scanf("%lf",&p[i][j]);
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
a[i][j]=2.0*(p[i][j]-p[i+1][j]);
a[i][n+1]+=(p[i][j]*p[i][j]-p[i+1][j]*p[i+1][j]);
}
}
for(int i=1;i<=n;i++){
int maxn=i;
for(int j=i+1;j<=n;j++){
if(fabs(a[j][i])>fabs(a[maxn][i])){
maxn=j;
}
}
for(int j=1;j<=n+1;j++){
swap(a[i][j],a[maxn][j]);
}
for(int j=1;j<=n;j++){
if(j==i) continue;
double tmp=a[j][i]/a[i][i];
for(int k=i+1;k<=n+1;k++){
a[j][k]-=a[i][k]*tmp;
}
}
}
for(int i=1;i<=n;i++){
printf("%.3lf ",a[i][n+1]/a[i][i]);
}
}
例3
枚举删掉的方程,由剩余的方程联立求解即可。
注意判断浮点数及是否有唯一解。
#include<bits/stdc++.h>
using namespace std;
double a[105][105],b[105][105],w[105];
int flag=0,m[105],f[105][105],n,Num,k,x;
int solve(){
for(int i=1;i<=n;i++){
int maxn=i;
for(int j=i+1;j<=n;j++){
if(fabs(a[j][i])>fabs(a[maxn][i]))
maxn=j;
}
for(int j=1;j<=n+1;j++){
swap(a[i][j],a[maxn][j]);
}
if(!a[i][i]) return 0;
for(int j=1;j<=n;j++){
if(j==i) continue;
double tmp=a[j][i]/a[i][i];
for(int k=i+1;k<=n+1;k++){
a[j][k]-=a[i][k]*tmp;
}
}
}
int maxn=-1,num=0;
for(int i=1;i<=n;i++){
double ans=a[i][n+1]/a[i][i];
if(ans<=0) return 0;
if(ans!=(int)ans) return 0;
maxn=max(maxn,(int)ans);
}
for(int i=1;i<=n;i++){
int ans=a[i][n+1]/a[i][i];
if(maxn==ans){
if(num) return 0;
num=i;
}
}
return num;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n+1;i++){
scanf("%d",&k);
for(int j=1;j<=k;j++){
scanf("%d",&x);
b[i][x]=1;
}
scanf("%lf",&b[i][n+1]);
}
for(int i=1;i<=n+1;i++){
swap(b[i],b[n+1]);
for(int j=1;j<=n;j++){
for(int k=1;k<=n+1;k++){
a[j][k]=b[j][k];
}
}
swap(b[i],b[n+1]);
Num=solve();
if(Num){
if(flag){
cout<<"illegal\n";
return 0;
}
flag=Num;
}
}
if(!flag){
cout<<"illegal\n";
return 0;
}
else cout<<flag<<endl;
}