UVA 1279 - Asteroid Rangers

昨天一题都没做,罪过罪过......

题目中首先读入顶点的信息,然后按照顶点的信息,每每两个顶点进行组合,合成一条新的边,在边当中存入对应的两个顶点的编号信息以及边的长度信息,将边按照长度的大小进行排序,便于后面对事件(也就是出现哪些边相等的时间信息以及对应的边的编号信息)信息进行处理以及记录,对事件的排序以时间为基准,在事件信息处理结束之后,就首先利用克鲁斯卡尔算法求出对应的最小生成树,然后依次处理每个事件,也就是出现某两条边相等的情况。对于事件的处理,首先要确定一点:该事件对应的老边已经在当前的最小生成树当中,新的一条边不在最下生成树当中,这样才能够用新的边来代替老的边。然后对于除老边之外的所有边改变相应的并查集,然后试探新边是否能够加入,如果新边能够加入,那么计数加一,同时更新对应的最小生成树的信息,最后返回计数即可,具体实现见如下代码:

#include<iostream>
#include<vector>
#include<string>
#include<set>
#include<stack>
#include<queue>
#include<map>
#include<algorithm>
#include<cmath>
#include<iomanip>
#include<cstring>
#include<sstream>
#include<cstdio>
#include<deque>
#include<functional>
using namespace std;

const double EPS = 1e-6;

class Point{
public:
	double x, y, z;
	double vx, vy, vz;
};

class Edge{
public:
	int from, to;
	double a, b, c;
};

class Event{
public:
	int oldEdge, newEdge;
	double t;//time
};

bool compare(const Edge& a, const Edge& b){//按照长度排序
	return a.c < b.c;
}

bool compare2(const Event& a,const Event& b){//按照时间排序
	return a.t < b.t;
}

class Solve{
public:
	int n;
	Point point[55];
	Edge edge[2510];
	int amount_e;
	vector<Event> affair;

	double square(double a, double b){
		return (a - b)*(a - b);
	}

	void Init(){
		amount_e = 0;
		affair.clear();
		for (int i = 1; i <= n; i++){
			cin >> point[i].x >> point[i].y >> point[i].z >> point[i].vx >> point[i].vy >> point[i].vz;
		}
		for (int i = 1; i < n; i++){
			for (int j = i + 1; j <= n; j++){
				amount_e++;
				edge[amount_e].from = i;
				edge[amount_e].to = j;
				edge[amount_e].a = square(point[i].vx, point[j].vx) + square(point[i].vy, point[j].vy)
					+ square(point[i].vz, point[j].vz);
				edge[amount_e].b = 2 * ((point[i].x - point[j].x)*(point[i].vx - point[j].vx) + (point[i].y - point[j].y)
					*(point[i].vy - point[j].vy) + (point[i].z - point[j].z)*(point[i].vz - point[j].vz));
				edge[amount_e].c = square(point[i].x, point[j].x) + square(point[i].y, point[j].y) +
					square(point[i].z,point[j].z);
			}
		}
		sort(edge + 1, edge + amount_e + 1, compare);
		for (int i = 1; i < amount_e; i++){
			for (int j = i + 1; j <= amount_e; j++){
				Event temp;
				int e1 = i, e2 = j;
				if (edge[e1].a < edge[e2].a) swap(e1,e2);
				double a = edge[e1].a - edge[e2].a;
				double b = edge[e1].b - edge[e2].b;
				double c = edge[e1].c - edge[e2].c;
				if (fabs(a) < EPS){//b*t+c=0; 
					if (fabs(b) < EPS) continue;
					if (b > 0){ swap(e1, e2); b = -b; c = -c; }
					if (c > 0){
						temp.oldEdge = e2, temp.newEdge = e1, temp.t = -c / b;
						affair.push_back(temp);
					}
				}
				else{
					double delta = b*b - 4 * a*c;
					if (delta < EPS) continue;
					double t1, t2;
					delta = sqrt(delta);
					t1 = (-b - delta) / (2 * a);
					t2 = (-b + delta) / (2 * a);
					if (t1 > 0){
						temp.t = t1;
						temp.oldEdge = e2;
						temp.newEdge = e1;
						affair.push_back(temp);
					}
					if (t2>0){
						temp.t = t2;
						temp.oldEdge = e1;
						temp.newEdge = e2;
						affair.push_back(temp);
					}
				}
			}
		}
		sort(affair.begin(),affair.end(),compare2);
	}

	int root[55];

	int find_root(int i){
		if (root[i] == i) return i;
		return root[i]=find_root(root[i]);
	}

	int Deal(){
		int ans = 1;
		Init();
		int cnt = 0;
		int pos[2500],e[2500];
		memset(pos,0,sizeof(pos));
		for (int i = 1; i <= n; i++) root[i] = i;
		for (int i = 1; i <= amount_e; i++){//i是边的编号
			int root1 = find_root(edge[i].from);
			int root2 = find_root(edge[i].to);
			if (root1 == root2) continue;
			root[root1] = root2;
			cnt++;
		    e[cnt] = i;//e代表的是第cnt个位置对应的是第i条边
			pos[i] = cnt;//pos[i]代表的是选取了第i条边
			if (cnt == n - 1) break;
		}
		for (int i = 0; i < affair.size(); i++){
			if (!pos[affair[i].oldEdge]) continue;
			if (pos[affair[i].newEdge]) continue;//保证老边选了  新边还没选  所以可以进行一轮变化
			for (int t = 1; t <= n; t++) root[t] = t;//下一轮判断新边是否能够加入
			int old = pos[affair[i].oldEdge];
			for (int j = 1; j < n; j++){
				if (j == old) continue;
				int id = e[j];
				int root1 = find_root(edge[id].from);
				int root2 = find_root(edge[id].to);
				if (root1 != root2) root[root1] = root2;
			}
			int id2 = affair[i].newEdge;
			int root1 = find_root(edge[id2].from);
			int root2 = find_root(edge[id2].to);
			if (root1 == root2) continue;
			ans++;
			root[root1] = root2;
			pos[affair[i].oldEdge] = 0;
			pos[affair[i].newEdge] = old;
			e[old] = affair[i].newEdge;
		}
		return ans;
	}
};

int main(){
	Solve a;
	int Case = 0;
	int res;
	while (cin >> a.n){
		Case++;
		res=a.Deal();
		cout << "Case " << Case << ": " << res << endl;
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值