题解 洛谷P5027 【Barracuda】(高斯消元)
题面:
题目背景
小正方形的冒险旅途,并不顺利。
一路上,小正方形看到了壮美秀丽的小岛被污染,看到了雄伟壮观的火山,还碰到了许许多多的敌人。
眼下,小正方形正在对付一个巨大的三角形。
题目描述
大三角形给小正方形讲起自己的过去:过去的它是一个挖宝工,后来被黑暗之主污染才会落到此番境地。
它也希望小正方形去战胜黑暗之主,不过限于黑暗之主的眼线密布,因此必须给小正方形设置障碍才能骗过那些“眼线”。
他给小正方形的问题是:它有 n 个小三角形,每个小三角形有一定的质量,它对这些三角形进行了 n + 1次称量,然而由于托盘天平(?)的问题,有一次称量的结果是有误的。
现在,大三角形想要知道最重的小三角形的 编号。
一组输入是合法的,当且仅当输入满足以下条件:
不存在一组 i,j,使得当我们假定第 i 条称量数据有误时能求出一种合法方案且我们假定第 jj 条称量数据有误时也能求出一种合法方案。
合法方案定义如下:
1、最重的三角形只有一个。
2、不存在重量不确定的三角形。
3、所有三角形的重量均为正整数。
输入格式
输入的第一行为一个正整数 nn,表示小三角形的数目。
接下来 n+1 行,每行按照以下格式输入:
首先是一个正整数 m,表示这次称量抓了几个小三角形。
接下来 m 个整数,表示称量的小三角形的编号。
最后一个整数 weight ,表示这次称量的结果。
输出格式
若合法,输出最重小三角形的编号,否则输出 "illegal"(不含引号)。
因为这道题很有趣 (其实是我毒了13遍才过) ,同时也为了写给自己供复习,于是决定写篇题解
首先,本题n<=100,而高斯消元n^3,如果暴力枚举错误的式子就是n^4,是卡得过去的
这道题的细节处理很多,由题意可以得出这样几个无解情况
1.判断答案是否为正整数
2.是否有多个最重三角形
3.重量不确定,即有多种删掉一个式子的情况合法
于是,上代码 (不喜勿喷)
#include<algorithm> #include<iostream> #include<iomanip> #include<cstring> #include<cstdio> #include<cmath> #include<queue> using namespace std; int n; int a[105][105]; double b[105][105]; int ans; int ansbj; int Gauss() { for(int i=1;i<=n;i++) { int no=i; for(int j=i+1;j<=n;j++)if(abs(b[j][i])>abs(b[no][i]))no=j; for(int j=1;j<=n+1;j++)swap(b[i][j],b[no][j]); double x=b[i][i]; for(int j=i;j<=n+1;j++)b[i][j]/=x; for(int j=1;j<=n;j++) { double y=b[j][i]; for(int k=1;k<=n+1;k++) if(i!=j) b[j][k]-=y*b[i][k]; } }//高斯消元模版,此时第i个未知数的解存在b[i][n+1]中 int num; for(int i=1;i<=n;i++) { num=0; for(int j=1;j<=n+1;j++)if(!b[i][j])num++; if(num==n+1)return 0;//全为0,无数解 } for(int i=1;i<=n;i++)if(b[i][n+1]<=0)return 0;//是否为正数 for(int i=1;i<=n;i++)if(b[i][n+1]!=(int)b[i][n+1])return 0;//是否为整数 double maxx=-0x3f3f3f3f;int bjbj=0; for(int i=1;i<=n;i++)maxx=max(maxx,b[i][n+1]);//求出最重三角形 for(int i=1;i<=n;i++)if(b[i][n+1]==maxx){if(bjbj)return 0;maxx=b[i][n+1];bjbj=i;}//判断是否有多个最重三角形 //如果到这里还没有return就说明答案合法 return bjbj;//返回最重三角形的编号 } int main() { int k,x; scanf("%d",&n); for(int i=1;i<=n+1;i++) { scanf("%d",&k); for(int j=1;j<=k;j++) { scanf("%d",&x); a[i][x]=1; } scanf("%d",&a[i][n+1]); } for(int i=1;i<=n+1;i++) { swap(a[i],a[n+1]);//把错的式子换到第n+1行 for(int j=1;j<=n;j++) for(int k=1;k<=n+1;k++)b[j][k]=a[j][k];//将删去错误式子的矩阵存在b中 swap(a[i],a[n+1]);//换回去 int nobj=Gauss();//判断是否合法 if(nobj) { if(ansbj){printf("illegal");return 0;}//有多种情况合法 ans=nobj;ansbj=1;// } } if(!ansbj)printf("illegal");else printf("%d",ans);//判断是否有合法结果 return 0; }