我有话说:
这道题紫书上给出的提示是,在一个新最小生成树生成的时候一定会有两条边欧几里德距离相等。这是因为按照题意,任何情况下,形成的二叉树只有一种,即所有的边长度都不等。所以我们先按照题目给出的数据计算预判发生交换的时间点,我们称之为事件点,按照发生时间先后进行排序。并且逐个检验,是否有新的二叉树生成。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#include <vector>
#include <cmath>
using namespace std;
const int maxn=50+5;
const int maxks=maxn*(maxn+1)*2;
const double eps=1e-8;
int n,nks;
struct Event{
double t;
int newks,oldks;
Event(double t=0,int newks=0,int oldks=0):t(t),newks(newks),oldks(oldks){}
bool operator<(const Event& rhs)const{
return t-rhs.t<0;
}
};
vector<Event>events;
struct KineticPoint{
double x,y,z;
double dx,dy,dz;
void read(){
scanf("%lf%lf%lf%lf%lf%lf",&x,&y,&z,&dx,&dy,&dz);
}
}kp[maxn];
struct KineticSegment{
double a,b,c;
int u,v;
bool operator<(const KineticSegment& rhs)const{
return c-rhs.c<0;
}
}ks[maxks];
inline double sqr(double x){return x*x;}
int pa[maxn];
void init_ufset(){for(int i=0;i<n;i++)pa[i]=i;}
int findset(int x){return pa[x]==x?x:pa[x]=findset(pa[x]);}
void make_segments(){
nks=0;
for(int i=0;i<n;i++){
for(int j=i+1;j<n;j++){
//距离计算为dist=sum{((kp[i].dx-kp[j].dx)*t+(kp[i].x-kp[j].x))^2}
//换算成二次函数的一般形式:at^2+b^t+c;
ks[nks].a=sqr(kp[i].dx-kp[j].dx)+sqr(kp[i].dy-kp[j].dy)+sqr(kp[i].dz-kp[j].dz);
ks[nks].b=2*((kp[i].x-kp[j].x)*(kp[i].dx-kp[j].dx)+(kp[i].y-kp[j].y)*(kp[i].dy-kp[j].dy)+(kp[i].z-kp[j].z)*(kp[i].dz-kp[j].dz));
ks[nks].c=sqr(kp[i].x-kp[j].x)+sqr(kp[i].y-kp[j].y)+sqr(kp[i].z-kp[j].z);
ks[nks].u=i;
ks[nks].v=j;
nks++;
}
}
sort(ks,ks+nks);
}
void make_events(){
events.clear();
for(int i=0;i<nks;i++){
for(int j=i+1;j<nks;j++){
int s1=i,s2=j;
if(ks[s1].a-ks[s2].a<0)s1=j,s2=i;//s1更大
double a=ks[s1].a-ks[s2].a;
double b=ks[s1].b-ks[s2].b;
double c=ks[s1].c-ks[s2].c;
if(fabs(a)<eps){//bt+c=0;
if(fabs(b)<eps)continue;
if(b>0){swap(s1,s2);b=-b;c=-c;}
if(c>0){events.push_back(Event(-c/b,s1,s2));}
continue;
}
double delta=b*b-4*a*c;
if(delta<eps)continue;
delta=sqrt(delta);
double t1=(-b-delta)/(a*2);
double t2=(-b+delta)/(a*2);//t1<t2
if(t1>0)events.push_back(Event(t1,s1,s2));
if(t2>0)events.push_back(Event(t2,s2,s1));
}
}
sort(events.begin(),events.end());
}
int solve(){
int pos[maxks];//pos[i]是在最小生成树中第i个 segment。0表示其不在树中。
int e[maxn];//e[i]表示在最小生成树中的第i个edge。pos[e[i]]=i;
init_ufset();
for(int i=0;i<nks;i++)pos[i]=0;
int idx=0;
for(int i=0;i<nks;i++){
int u=findset(ks[i].u),v=findset(ks[i].v);
if(u!=v){
e[pos[i]=++idx]=i;
pa[u]=v;
}
if(idx==n-1)break;
}
int ans=1;
for(int i=0;i<events.size();i++){
if(pos[events[i].oldks]&&(!pos[events[i].newks])){
init_ufset();
int oldpos=pos[events[i].oldks];
for(int j=1;j<n;j++){
if(j!=oldpos){
int u=findset(ks[e[j]].u),v=findset(ks[e[j]].v);
if(u!=v){
pa[u]=v;
}
}
}
int u=findset(ks[events[i].newks].u),v=findset(ks[events[i].newks].v);
if(u!=v){//新的二叉树。
ans++;
pos[events[i].newks]=oldpos;
e[oldpos]=events[i].newks;
pos[events[i].oldks]=0;
}
}
}
return ans;
}
int main()
{
int kase=0;
while(scanf("%d",&n)==1){
for(int i=0;i<n;i++)kp[i].read();
make_segments();
make_events();
int ans=solve();
printf("Case %d: %d\n",++kase,ans);
}
return 0;
}