Amphiphilic Carbon Molecules
Shanghai Hypercomputers, the world's largest computer chip manufacturer, has invented a new class of nanoparticles called Amphiphilic Carbon Molecules (ACMs). ACMs are semiconductors. It means that they can be either conductors or insulators of electrons, and thus possess a property that is very important for the computer chip industry. They are also amphiphilic molecules, which means parts of them are hydrophilic while other parts of them are hydrophobic. Hydrophilic ACMs are soluble in polar solvents (for example, water) but are insoluble in nonpolar solvents (for example, acetone). Hydrophobic ACMs, on the contrary, are soluble in acetone but insoluble in water. Semiconductor ACMs dissolved in either water or acetone can be used in the computer chip manufacturing process.As a materials engineer at Shanghai Hypercomputers, your job is to prepare ACM solutions from ACM particles. You go to your factory everyday at 8 am and find a batch of ACM particles on your workbench. You prepare the ACM solutions by dripping some water, as well as some acetone, into those particles and watch the ACMs dissolve in the solvents. You always want to prepare unmixed solutions, so you first separate the ACM particles by placing an Insulating Carbon Partition Card (ICPC) perpendicular to your workbench. The ICPC is long enough to completely separate the particles. You then drip water on one side of the ICPC and acetone on the other side. The ICPC helps you obtain hydrophilic ACMs dissolved in water on one side and hydrophobic ACMs dissolved in acetone on the other side. If you happen to put the ICPC on top of some ACM particles, those ACMs will be right at the border between the water solution and the acetone solution, and they will be dissolved. Fig.1 shows your working situation.


Input
There will be no more than 10 test cases. Each case starts with a line containing an integer N, which is the number of ACM particles in the test case. N lines then follow. Each line contains three integers x, y, r, where (x, y) is the position of the ACM particle in the 2D picture and r can be 0 or 1, standing for the hydrophilic or hydrophobic type ACM respectively. The absolute value of x, y will be no larger than 10000. You may assume that N is no more than 1000. N = 0 signifies the end of the input and need not be processed. Fig.2 shows the positions of ACM particles and the best ICPC position for the last test case in the sample input.
Output
For each test case, output a line containing a single integer, which is the maximum number of dissolved ACM particles.
Sample Input
3
0 0 0
0 1 0
2 2 1
4
0 0 0
0 4 0
4 0 0
1 2 1
7
-1 0 0
1 2 1
2 3 0
2 1 1
0 3 1
1 4 0
-1 2 0
0
Sample Output
3
3
6
这道题思考了整整两天,网上也没有特别优秀的代码或者是讲解。所以,我直接读了Liu的代码。
在阅读Liu的代码之前,我先使用了暴力枚举的方法进行解决,由于分割线在点上比不再点上取得的答案要更优,也就是,我们枚举任意两点,以其所在直线为分割线,计算分割线上侧与下侧的亲水点和疏水点,然后计算线上的点,最后得出最优的答案。时间复杂度,O(N^3)。
上面的方法显然是超时的。我们可以枚举一个点,以这个点为原点建立新的坐标系,然后旋转过该点的直线并扫描一周,扫描的过程中,要随时维护直线两侧的点数。时间复杂度,O((N^2)*logN)
这个算法说起来倒是容易,但是真正做起来需要抽象出许多东西,并使用很多技巧。下面一一列举:
1.建立新的坐标系,以基准点为新的坐标系原点,将其他点的坐标与基准点元坐标相减,得到其他点在新坐标系中的坐标;
2.atan2(y,x)函数计算点(x,y)在坐标系中以原点为极坐标系原点、x轴为极轴的极角(单位为rad);
3.将亲水点和疏水点统一起来,方法是将疏水点关于新坐标系的原点对称,原本直线上侧的疏水点到了下侧,下侧的疏水点到了上侧,这样在计算的时候就可以只计算直线一侧的点,并且,直线一侧的所有点都是合法的点;
4.因为要对所有点进行扫描,所以,按照极角进行排序,扫描的时候只记录直线一侧的点数,并动态的维护;
5.扫描时有一个起始向量,有一个终止向量,枚举起始向量,并同时移动终止向量以便维护直线单侧的点数;
6.判断终止向量的终止条件是两向量夹角大于180度且终止向量不等于起始向量时终止,判断条件在代码中给出,证明与推导过程在此不再给出,弱国读者们想自己证明,那么可以分析A.x和B.x的正负号,并结合单位圆与tan函数图象来分析。
UVaoj AC 882ms | Poj AC 1932ms | HDUoj AC 1316ms
附代码如下:
暴力解法:
#include<cstdio>
#include<algorithm>
using namespace std;
#define MAXN (1000+5)
struct MOLECULE{
int x,y,type;
};
int n;
MOLECULE m[MAXN];
int solve(MOLECULE a,MOLECULE b){
//点向式写方程,点(x0,y0),向量(x0-x1,y0-y1);
//则有方程:(x-x0)/(x0-x1)=(y-y0)/(y0-y1);
//变形有:(y0-y1)*(x-x0)-(x0-x1)*(y-y0)=0;
//若P点坐标带入方程,则若=0在直线上,<>0在两侧,其中设>0在L侧,<0在R侧。
int x0=a.x,y0=a.y,x1=b.x,y1=b.y;
int L0=0,R0=0,L1=0,R1=0,LINE=0;
for(int i=0;i<n;i++){
int xx=m[i].x,yy=m[i].y,tp=m[i].type;
int flag=(y0-y1)*(xx-x0)-(x0-x1)*(yy-y0);
if(!flag)LINE++;
if(flag>0){if(tp)L1++;if(!tp)L0++;}
if(flag<0){if(tp)R1++;if(!tp)R0++;}
}
return max(L1+R0,R1+L0)+LINE;
}
int main(){
while(scanf("%d",&n)==1&&n){
for(int i=0;i<n;i++)scanf("%d%d%d",&m[i].x,&m[i].y,&m[i].type);
int ans=0;
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(i==j)continue;
MOLECULE a=m[i],b=m[j];
ans=max(ans,solve(a,b));
}
}
printf("%d\n",ans);
}
return 0;
}
AC解法:
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
#define MAXN (1000+5)
struct NODE{
int x,y;
double rad;
bool operator < (const NODE& rhs) const {return rad<rhs.rad;}
};
int n,type[MAXN];
NODE m[MAXN],a[MAXN];
bool LEFT(NODE A,NODE B){
return A.x*B.y-B.x*A.y>=0;
}
int solve(){
if(n<=2)return n;
int ans=0;
for(int i=0;i<n;i++){
int k=0;
for(int j=0;j<n;j++){
if(i!=j){
a[k].x=m[j].x-m[i].x;
a[k].y=m[j].y-m[i].y;
if(type[j]){a[k].x=-a[k].x;a[k].y=-a[k].y;}
a[k].rad=atan2(a[k].y,a[k].x);
k++;
}
}
sort(a,a+k);
int L=0,R=0,cnt=2;
while(L<k){
if(R==L){R=(R+1)%k;cnt++;}
while(R!=L&&LEFT(a[L],a[R])){R=(R+1)%k;cnt++;}
cnt--;
L++;
ans=max(ans,cnt);
}
}
return ans;
}
int main(){
//freopen("in.txt","r",stdin);
while(scanf("%d",&n)==1&&n){
for(int i=0;i<n;i++)scanf("%d%d%d",&m[i].x,&m[i].y,&type[i]);
printf("%d\n",solve());
}
//fclose(stdin);
return 0;
}