题目链接:https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=4153
题目大意:
第一行给出两个数字,n,r(这个圆的半径,感觉这个条件可有可无啊,反正我是没用上)
下面n行给出点的位置(是角度制的),这些点都是位于圆心是坐标原点的圆上。
任取三个点可以组合成ans个锐角三角形,要求出ans并输出。
解题思路:
还是老样子.....没思路.....自己脑子里想的就是暴力,但是肯定会超时,所以没敢动手写下去了。后来请教同学,学习了一个很巧妙的方法。如下
锐角三角形的个数=所有的三角形--钝角三角形--直角三角形,那么现在就直接转换成为找钝角三角形和直角三角形。
几何规律:如果在圆上任取三点,构成一个直角三角形,那么圆形肯定位于三角形的斜边上;如果构成一个钝角三角形,那么圆形一定在该三角形之外;构成锐角三角形,圆心则在三角形内部。
先把所有的点排序,然后依次遍历每个点,例如目前遍历到了i点(该点的角度值为p[i]),则找出p[i]+180的点的位置,判断这两个点之间有多少个点(如果p[i]+180点的位置上刚好有一个点与之重合,那么这个点应该也应该被包括),如果有m个点,那么从这m个点中,任意选取两个点,那么这两个点与i点一定会构成一个钝角或者直角三角形。
虽然分析完之后感觉应该不会太难,但是我至少wa了30遍才过掉这个题.....
#include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<string> #include<stack> #include<queue> #include<vector> #include<algorithm> #include<iostream> #define maxn 40000+20//这里要开大一点 #define eps 1e-6//记得要加上它,我在这里也贡献了一次wa using namespace std; #ifdef __int64 typedef __int64 LL; #else typedef long long LL; #endif LL n,r;//这个题目记得要用LL double p[maxn]; LL bs(int i)//查找出两个点之间有多少个点 { LL low=i; LL high=2*n-1; while(low<high) { LL mid=(low+high+1)>>1; if(p[mid]<=p[i]+180.0+eps) { low=mid; } else { high=mid-1; } } return low-i; } int main() { LL Ans; int cnt=0; while(scanf("%lld%lld",&n,&r)&&n&&r) { Ans=n*(n-1)*(n-2)/6;//题目中给出的算法,求总的三角形的个数 for(int i=1;i<=n;i++) scanf("%lf",&p[i]); sort(p+1,p+1+n); for(int i=1;i<=n;i++)//这个是一个很巧妙的处理方法,可以学习一下 p[i+n]=p[i]+360.0; LL ans=0; for(int i=1;i<=n;i++) { LL s=bs(i); ans+=s*(s-1)/2;//求出这些点能够组成多少个钝角或者直角三角形 } printf("Case %d: %lld\n",++cnt,Ans-ans); } return 0; }