题目大意:见刘汝佳《算法竞赛入门经典——训练指南》P173
解题思路:
先求出对于每一个点,有多少个三角形包含它,把各个点得到的数值加起来的总和除以 C[n][3] 即可得出答案。对于每一个点,可以求出有多少个三角形不包含它,设为tmp,C[n-1][2] - tmp = 包含这个点的三角形数。那么现在的问题就是如何求出不包含这个点的三角形数:
我们先把指定的这个点作为原点将其他所有点进行极角排序,从0开始枚举排序后的每一个点,把原点到枚举的点的射线作为一个平角的一条边,那么所有在这个平角的一侧的点与枚举的点组成的三角形都不会经过原点。好吧,这部分好难讲,而且我又很菜很菜,只能在代码里面尽量作些注释。
AC代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath> using namespace std;
typedef long long ll;
const int maxn=+;
const double PI=acos(-1.0);
struct point{
double x,y;
}inp[maxn];
int n;
double r[maxn<<];
double s;
double counts(int d){
int cnt=;
for(int i=;i<n;i++){
if(i==d) continue;
double tp=atan2(inp[i].y-inp[d].y,inp[i].x-inp[d].x);
r[cnt]=tp;
r[cnt+n-]=tp+*PI; //这里对每一个角多做了一个投影,防止后面的点找不到180度以内的点
cnt++;
}
cnt=*n-;
sort(r,r+cnt);
double ans=;
int mv=;
for(int i=;i<n-;i++){
double tmp=r[i]+PI;
while(tmp>r[mv]) mv++; //这个mv有效的防止了重复计算
double cnt=mv-i-;
ans+=cnt*(cnt-)/;
}
return s-ans;
}
int main(){
int kase=;
while(scanf("%d",&n)==&&n){
for(int i=;i<n;i++) scanf("%lf %lf",&inp[i].x,&inp[i].y);
s=(double)(n-)*(n-)*(n-)/6.0; //C[n-1][3]
double ans=0.0;
double sum=(double)n*(n-)*(n-)/6.0; //C[n][3]
for(int i=;i<n;i++)
ans+=counts(i);
printf("City %d: %.2lf\n",kase++,ans/sum);
}
return ;
}