点击打开链接
题意:
题意好难懂....
在二维平面上给你n(<100)个点.让你将这些点分为A,B两个集合,如果无论你怎么划线都不能使得所有的A在直线一侧,B在另一侧就输出YES,并输出你给定的方案,否则就输出NO。(划线不能经过给定点)
思路:
首先我们知道N<=2 一定不满足,当n==3时,如果三点不共线(即三角形,也是三个点的凸包)一定也不满足.
如果三点贡献我们就让中间的点为一个颜色,其余的两个一个颜色就可以了.
对于其余的情况要想不能使A B在不同侧就要无论怎么划线两边都有才可以.这种情况下我们可以求一个凸包,凸包上的点全部一个颜色,凸包内的点另一个颜色,这样就使得无论怎么划线都把凸包分为两部分,永远满足题意.
但是要注意如果所有的点都在凸包上那么我们就随便找两个不相邻的点一个颜色,其余的另一种颜色就可以啦。
#include
#include
#include
using namespace std;
typedef long long ll;
const int mod=1e9+7;
const int maxn=5e3+5;
struct node
{
int x,y;
int id ;
}a[maxn],sta[maxn];
int head;
char s[123];
//求亮点之间距离的平方.
int dis(node n1,node n2)
{
return (n1.x-n2.x)*(n1.x-n2.x)+(n1.y-n2.y)*(n1.y-n2.y);
}
//计算叉积
int det(int x1,int y1,int x2,int y2)
{
return x1*y2-x2*y1;
}
//判断点和直线的方向用叉积
//若P1×p2 >0 则p1在p2的顺时针方向.
//<0 p1在p2的逆时针方向
//= 0 共线
int cross(node a,node n1,node n2)
{
return det(n1.x-a.x,n1.y-a.y,n2.x-a.x,n2.y-a.y);
}
//极角排序
bool cmp(node n1,node n2)
{
//a[0]为纵坐标最小的点一定在凸包上,以它为原点进行极角排序
int k = cross(a[0],n1,n2);
if(k > 0) return 1;//角度小的排在前面
//若共线则距离a[0]距离近的在前面.
if(k == 0 && dis(a[0],n1) < dis(a[0],n2))
return 1;
return 0;
}
//最后栈中存的点就是凸包上的点.
void Graham(int n)
{
int i;
for(i = 1;i < n;++i) //先找到最下面的那个点作为原点.
if(a[i].x < a[0].x || (a[i].x == a[0].x && a[i].y < a[0].y))
swap(a[0],a[i]);
sort(a+1,a+n,cmp);//极角排序
a[n] = a[0];
sta[0] = a[0];//最下面的一个点和第二个点一定在凸包上.
sta[1] = a[1];
head = 1;
//Graham 算法
for(int i = 2;i < n;++i)
{
while(head)
{
int fu = cross(sta[head - 1],sta[head],a[i]);
if(fu < 0)
head--;
else if(fu == 0 && dis(sta[head - 1],a[i]) > dis(sta[head - 1],sta[head]))
head--;
else
break;
}
sta[++head] = a[i];
}
}
int main(){
int n;
int _;
cin>>_;
while(_--)
{
scanf("%d",&n);
for(int i = 0;i < n;++i)
{
scanf("%d %d",&a[i].x,&a[i].y);
a[i].id=i;
}
if(n <= 2)
{
puts("NO");continue;
}
Graham(n);
if(n == 3 && head + 1 == n)
{
puts("NO");
continue;
}
for(int i = 0;i < n;++i) s[i] = 'B';
s[n] = 0;
if(n != head + 1)
for(int i = 0;i <= head;++i)
s[sta[i].id] = 'A';
else
s[sta[0].id] = 'A',s[sta[2].id] = 'A';
puts("YES");
puts(s);
}
return 0;
}