1. 程式人生 > >UVA 109 SCUD Busters【凸包模擬題】

UVA 109 SCUD Busters【凸包模擬題】

題目大意:世界由幾個互不重疊領土但彼此敵對的國家組成,每個國家有一個發電站,負責給本國發電。

                    1,給出每個國家的建築數(包括髮電站和房子數),每個國家用最少的圍牆將本國保護起來(凸包);

                    2,現在有不定數量的飛毛腿導彈開始襲擊這些國家,給出導彈落地點,落在某國區域內,某國直接玩完,以該國面積為大小的區域不能發電;

                    3,求出最後所有不能發電區域的總面積;

解題策略:

                    演算法思路:求出每個國家的凸包並計算面積——導彈挨個判定在不在凸包內——若在,則累加當前凸包面積

                    注意:若某國家之前被導彈襲擊過,之後再被襲擊的話,面積只能算一次;

PS:這題臨睡前抱著試試的態度做了下,按照自己的理解,設計好資料結構和演算法,沒想到1A,時間已是2:00,爽爆了!!!有木有!!!

          明天一早還要編譯原理,目測明天事情也比較多,苦逼了,速度睡覺!!!

/*
   UVA 109 SCUD Busters
   AC by J_Dark
   ON 2013/5/7 2:20    根本沒想到會1A啊!!!!!爽爆了!!!!!!
   Time 0.015s
*/
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
const int maxn = 110;
/////////////////////////////////////////
struct point{
	double x, y;
	point(double a, double b){
		x = a;
		y = b;
	}
};
struct Kingdom{
	double area;
	bool use;
	int top;
	vector<int> CH;
	Kingdom(){
	   area = 0;
	   top = 1;
	   CH.clear();
	   CH.resize(maxn);
	   CH[0] = 0;
	   CH[1] = 1;
	   use = false;  //未被導彈襲擊
	}
};
vector<point> P, M; //臨時點集  導彈
vector< vector<point> > kdNode; //記錄國家點的資訊
vector<Kingdom> KD;  //國家
int nodeNum;
double ansArea=0;  //不能發電區域面積
/////////////////////////////////////////
void Input(){
	P.clear();
	//M.clear();
	//CH.clear();
	double xx, yy;
	if(nodeNum != -1){
       for(int i=0; i<nodeNum; i++){
		  cin >> xx >> yy;
	      P.push_back(point(xx, yy));
	   }
	   kdNode.push_back(P);
	}
	else{
	   while(cin >> xx >> yy)
	      M.push_back(point(xx, yy));
	}
}

//求凸包
bool cmp(point a, point b){
	if(a.y == b.y)  return a.x < b.x;
	return a.y < b.y;
}
//判斷向量p2-pp是否在向量p1-p2右側
bool turnRight(point p1, point p2, point pp){
    const double eps = 1e-20;
	if((p2.x - p1.x)*(pp.y - p2.y) - (pp.x - p2.x)*(p2.y - p1.y) <= eps) return true;
	return false;
}
//計算叉積
double multi(point p0, point p1, point p2){
	return (p1.x - p0.x)*(p2.y - p0.y) - (p2.x - p0.x)*(p1.y - p0.y);
}

void Compute(){
	//計算每個國家的凸包
	//cout << kdNode.size() << endl << endl;
    for(int k=0; k<kdNode.size(); k++){
		sort(kdNode[k].begin(), kdNode[k].end(), cmp);
		KD.push_back(Kingdom());
		//從起點0到到排序最後點作凸包右鏈  過程1
		for(int i=2; i<kdNode[k].size(); i++){
			while( KD[k].top && turnRight(kdNode[k][KD[k].CH[KD[k].top-1]], kdNode[k][KD[k].CH[KD[k].top]], kdNode[k][i]) )
			{
			   KD[k].top--;
			}
			KD[k].CH[++KD[k].top] = i;
		}

		int len = KD[k].top;
		//從排序最高點到到起點0fab反向作凸包右鏈  過程2
		KD[k].CH[++KD[k].top] = kdNode[k].size()-2;
		for(int i=kdNode[k].size()-3; i>=0; i--){
			//KD[k].top!=len, 不考慮已在過程1生成凸包上的點
			while( KD[k].top!=len && turnRight(kdNode[k][KD[k].CH[KD[k].top-1]], kdNode[k][KD[k].CH[KD[k].top]], kdNode[k][i]) )
			{
			   KD[k].top--;
			}
			KD[k].CH[++KD[k].top] = i;
		}

		//計算每個國家凸包面積
        for(int i=1; i<KD[k].top-1; i++){
			KD[k].area += multi(kdNode[k][KD[k].CH[0]], kdNode[k][KD[k].CH[i]], kdNode[k][KD[k].CH[i+1]]);
		}
		//printf("KD[%d].area = %lf\n", k, KD[k].area);

		//判斷導彈是否襲擊當前國家
		for(int m=0; m<M.size(); m++){
		   if(KD[k].use)  break;  //該國家之前已被導彈摧毀
		   for(int i=0; i<KD[k].top-1; i++){
			   if(!turnRight(kdNode[k][KD[k].CH[i]], kdNode[k][KD[k].CH[(i+1)%KD[k].top]], M[m]))//若點M[m]在凸包邊左側
				  KD[k].use = true; //該國家可能會被襲擊
			   else{
			      KD[k].use = false; //點M[m]不在凸包內部,導彈無法襲擊
				  break;
			   }
		   }
		   if(KD[k].use){  //該國家已被當前導彈摧毀
			   ansArea += KD[k].area;
			   break;
		   }
		}
	}
	printf("%.2lf\n", ansArea/2);  //由於按方向分解凸包時,叉積計算為三角形有效面積2倍,故總和應除以2
}


/////////////////////////////////////////
int main(){
	while(cin >> nodeNum)
	{
	   Input();
	   if(nodeNum == -1)  Compute();
	}
	return 0;
}