Codeforces 1045E. Ancient civilizations 构造 计算几何 凸包

原文链接https://www.cnblogs.com/zhouzhendong/p/CF1045E.html

4K码量构造题,CF血腥残暴!

题解

首先,如果所有点颜色相同,那么直接连个菊花搞定。

然后我们建个凸包。

如果凸包上有大于2段颜色(就是至少四段),比如这样

那么必然无解。

否则就只有一段颜色或者两段颜色:

这里我们先不管这个,考虑一个三角形的构造。

考虑三角形三个顶点颜色不全相同的情况,例如:

(两个白点的情况是等价的)

假如三角形区域内没有白点,那么直接全部连到其中一个黑点就好了。

否则假设里面有一个白点:

那么我们将白顶点连上这个中间的白点(将红色边加入最终答案),把原三角形划分成3个子问题递归求解即可。

回到原问题:

如果凸包上只有一种颜色,那么在里面找一个白点(因为已经排除全部都是黑点的情况了,所以必然有白点),如下图一样将红色边相连,按照红/黑色边将凸包三角划分成子问题解决即可:

如果凸包上有两种颜色,那么就类似地:

划分集合的暴力枚举点判定是否在三角形内就好了。

代码

#include <bits/stdc++.h>
#define clr(x) memset(x,0,sizeof (x))
using namespace std;
typedef long long LL;
LL read(){
	LL x=0,f=0;
	char ch=getchar();
	while (!isdigit(ch))
		f|=ch==‘-‘,ch=getchar();
	while (isdigit(ch))
		x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	return f?-x:x;
}
const int N=1005;
int n;
struct Point{
	int x,y;
	Point(){}
	Point(int _x,int _y){
		x=_x,y=_y;
	}
	friend Point operator + (Point A,Point B){
		return Point(A.x+B.x,A.y+B.y);
	}
	friend Point operator - (Point A,Point B){
		return Point(A.x-B.x,A.y-B.y);
	}
	friend bool operator == (Point A,Point B){
		return A.x==B.x&&A.y==B.y;
	}
	friend bool operator != (Point A,Point B){
		return A.x!=B.x||A.y!=B.y;
	}
}O;
int cross(Point A,Point B){
	return A.x*B.y-B.x*A.y;
}
int cross(Point A,Point B,Point C){
	return cross(B-A,C-A);
}
int Dot(Point A,Point B){
	return A.x*B.x+A.y*B.y;
}
int Dis(Point A,Point B){
	return Dot(A-B,A-B);
}
struct civ{
	Point p;
	int c,id;
	civ(){}
	civ(Point _p,int _c,int _id){
		p=_p,c=_c,id=_id;
	}
};
civ Get_civ(int id){
	Point p;
	p.x=read(),p.y=read();
	int c=read();
	return civ(p,c,id);
}
vector <civ> p,con;
bool cmpO(Point a,Point b){
	return a.y!=b.y?a.y<b.y:a.x<b.x;
}
bool cmpAngle_civ(civ a,civ b){
	int c=cross(O,a.p,b.p);
	return c?c>0:Dis(O,a.p)<Dis(O,b.p);
}
vector <civ> Get_Convex(vector <civ> p){
	vector <civ> st(0);
	int n=p.size();
	for (int i=1;i<n;i++)
		if (!cmpO(p[0].p,p[i].p))
			swap(p[0],p[i]);
	O=p[0].p;
	sort(p.begin()+1,p.end(),cmpAngle_civ);
	for (int i=0;i<n;i++){
		while (st.size()>1&&cross(st[st.size()-2].p,st.back().p,p[i].p)<=0)
			st.pop_back();
		st.push_back(p[i]);
	}
	return st;
}
int check_same(){
	for (int i=1;i<n;i++)
		if (p[i].c!=p[0].c)
			return 0;
	return 1;
}
int check_inside(Point A,Point B,Point C,Point P){
	if (P==A||P==B||P==C)
		return 0;
	int S1=abs(cross(A,B,C));
	int S2=abs(cross(P,A,B))+abs(cross(P,B,C))+abs(cross(P,C,A));
	return S1==S2;
}
vector <civ> get_inside(Point A,Point B,Point C,vector <civ> S){
	static vector <civ> res;
	res.clear();
	for (auto v : S)
		if (check_inside(A,B,C,v.p))
			res.push_back(v);
	return res;
}
vector <pair <int,int> > ans;
void solve(civ A,civ B,civ C,vector <civ> S){
	int flag=0;
	civ p;
	for (auto c : S)
		if (c.c!=A.c){
			flag=1,p=c;
			break;
		}
	if (!flag){
		for (auto c : S)
			ans.push_back(make_pair(A.id,c.id));
		return;
	}
	ans.push_back(make_pair(C.id,p.id));
	solve(A,B,p,get_inside(A.p,B.p,p.p,S));
	solve(C,p,A,get_inside(C.p,p.p,A.p,S));
	solve(C,p,B,get_inside(C.p,p.p,B.p,S));
}
int main(){
	n=read();
	for (int i=1;i<=n;i++)
		p.push_back(Get_civ(i));
	con=Get_Convex(p);
	int cnt=con[0].c^con.back().c;
	for (int i=1;i<con.size();i++)
		cnt+=con[i-1].c^con[i].c;
	if (cnt>2)
		return puts("Impossible"),0;
	ans.clear();
	if (cnt==0){
		if (check_same()){
			printf("%d\n",n-1);
			for (int i=1;i<n;i++)
				printf("%d %d\n",0,i);
			return 0;
		}
		for (int i=1;i<con.size();i++)
			ans.push_back(make_pair(con[i-1].id,con[i].id));
		civ mid;
		for (auto c : p)
			if (c.c!=con[0].c){
				mid=c;
				break;
			}
		solve(con[0],con.back(),mid,get_inside(con[0].p,con.back().p,mid.p,p));
		for (int i=1;i<con.size();i++)
			solve(con[i-1],con[i],mid,get_inside(con[i-1].p,con[i].p,mid.p,p));
	}
	else {
		vector <civ> _con(0);
		int stco=con[0].c^1,i;
		int m=con.size();
		for (i=0;;i=(i+1)%m)
			if (con[i].c==stco){
				_con.push_back(con[i]);
				if (con[(i+1)%m].c!=stco)
					break;
			}
		stco^=1;
		for (i=(i+1)%m;;i=(i+1)%m)
			if (con[i].c==stco){
				_con.push_back(con[i]);
				if (con[(i+1)%m].c!=stco)
					break;
			}
		con=_con;
		for (i=0;i<m;i++)
			if (con[i].c!=con[(i+1)%m].c)
				break;
		int b=i;
		for (int i=0;i<b;i++)
			ans.push_back(make_pair(con[i].id,con[i+1].id));
		for (int i=b+1;i<m-1;i++)
			ans.push_back(make_pair(con[i].id,con[i+1].id));
		b++;
		for (int i=0;i<b-1;i++)
			solve(con[i],con[i+1],con[b],get_inside(con[i].p,con[i+1].p,con[b].p,p));
		for (int i=b;i<m-1;i++)
			solve(con[i],con[i+1],con[0],get_inside(con[i].p,con[i+1].p,con[0].p,p));
	}
	printf("%d\n",(int)ans.size());
	for (auto e : ans)
		printf("%d %d\n",e.first-1,e.second-1);
	return 0;
}

  

原文地址:https://www.cnblogs.com/zhouzhendong/p/CF1045E.html

时间: 2024-11-08 00:50:58

Codeforces 1045E. Ancient civilizations 构造 计算几何 凸包的相关文章

计算几何——凸包问题(三)

注:本文是2016年春季清华大学邓俊辉老师<计算几何>MOOC课程的简要个人总结系列之一,我将同步课程内容更新.不过有可能写的不完全是课程内容,也包含一些个人理解.如果你在看完本文后开始对计算几何感兴趣,请前往相应的MOOC平台完整学习邓老师的课程.如此精心设计和编排的课程,不应该被辜负.在此感谢邓老师! Knowledge Dependence: 阅读本文前你只需要有基本的几何知识和算法知识(本篇重点要求复杂度分析与选择排序算法)即可.代码实现需要一丢丢C++基础. 由于作者很懒不想画图,所

计算几何--凸包总结

计算几何凸包 凸包:给你n个散落的点,让你求出最小的凸多边形将所有的点包括起来,或者点在边上.用到的算法是Graham Graham算法:首先找到一个顶点作为基点,然后将这个点与其他点进行连线,然后按照角度的大小进行排序. 然后加点,第一个点肯定在凸多边形上,然后开始加点,每加一个点,要用向量的×积判断以前是不是有边在他的里面,如果有的话,就将这个点替换,这个过程是一个回溯的过程 首先找基点,纵坐标最小的一定在凸包上,如果纵坐标相等那么就选横坐标小的. 下面模拟一下Graham算法扫描的过程(截

计算几何 --- 凸包 模板

//Memory Time // 1347K 0MS // by : Snarl_jsb #include<algorithm> #include<cstdio> #include<cstring> #include<cstdlib> #include<iostream> #include<vector> #include<queue> #include<stack> #include<map> #

[笔记] 计算几何-凸包 POJ-3348 Cows

题目:http://poj.org/problem?id=3348 求凸包面积 算法:先对点的横坐标排序,从左到右先计算下凸边,再从右到左计算上凸边.复杂度比Graham Scan法稍稍要高(两次遍历点集),但实现较容易 #include <stdio.h> #include <algorithm> using namespace std; struct point{ double x,y; }; point a[10005]; point res[10005]; bool cmp

Codeforces 534C Polycarpus&#39; Dice 构造

题意:给你n个筛子,第 i 个筛子有 可以表示范围 1-a[i]的数,给你最后筛子和,问你每个筛子不可能的值有多少个. 解题思路:得到每个筛子的取值范围. 解题代码: 1 // File Name: c.cpp 2 // Author: darkdream 3 // Created Time: 2015年04月13日 星期一 00时38分58秒 4 5 #include<vector> 6 #include<list> 7 #include<map> 8 #includ

Codeforces 432E Square Tiling(构造+贪心)

我们通常这么写 using (SqlDataReader drm = sqlComm.ExecuteReader()) { drm.Read();//以下把数据库中读出的Image流在图片框中显示出来. MemoryStream ms = new MemoryStream((byte[])drm["Logo"]); Image img = Image.FromStream(ms); this.pictureBox1.Image = img; } 我的写数据 private void b

uva 10652 Board Wrapping (计算几何-凸包)

Problem B Board Wrapping Input: standard input Output: standard output Time Limit: 2 seconds The small sawmill in Mission, British Columbia, has developed a brand new way of packaging boards for drying. By fixating the boards in special moulds, the b

计算几何-凸包算法 Python实现与Matlab动画演示

凸包算法是计算几何中的最经典问题之一了.给定一个点集,计算其凸包.凸包是什么就不罗嗦了 本文给出了<计算几何——算法与应用>中一书所列凸包算法的Python实现和Matlab实现,并给出了一个Matlab动画演示程序. 啊,实现谁都会实现啦╮(╯▽╰)╭,但是演示就不一定那么好做了. 算法CONVEXHULL(P)  输入:平面点集P  输出:由CH(P)的所有顶点沿顺时针方向组成的一个列表 1.   根据x-坐标,对所有点进行排序,得到序列p1, …, pn 2.   在Lupper中加入p

hdu 1616 计算几何 凸包

题意是一个世界有许多个国家,每个国家有N个建筑,包括一个发电站和N-1个用电建筑,所有建筑围成的凸包就是这个国家的面积.一枚导弹如果在一个国家之内爆炸则可以使这个国家停电. step 1:求出每个国家的凸包(我用水平排序就是各种坑,改叉乘排序才过,主要是后面求面积的时候需要这个叉乘排序的信息). step 2:判断每枚导弹是否在这个国家的范围之内. step 3:求出所有停电的国家的面积. 就是计算几何的综合模拟水题,坑点就是要小心(QAQ||写计算几何的题目都是要小心). 传送门:http:/