1 条题解
-
0
ACM粒子溶解问题题解
一、题目分析
题目要求通过放置一块垂直隔板(ICPC)将ACM粒子分为两部分,使得溶解的粒子数最多。关键条件:
- 隔板位置:垂直隔板可放置在任意位置,将粒子分为左右两部分;
- 溶解规则:左边的亲水性粒子(r=0)和右边的疏水性粒子(r=1)会溶解,隔板上的粒子全部溶解。
二、算法思路
-
几何转换:
- 枚举每个粒子作为隔板上的点,将其他粒子相对于该点的坐标进行转换;
- 对于疏水性粒子,将其坐标取反(相当于旋转180度),使其与亲水性粒子的判断规则统一。
-
极角排序:
- 计算所有转换后的粒子的极角(atan2),并按极角排序;
- 使用双指针法统计在某个半平面内的粒子数,即溶解的最大粒子数。
三、代码实现
#include <iostream> #include <math.h> #include <algorithm> using namespace std; #define Point Vector const int maxn=1005; const double eps=1e-8; struct Point{ int x,y; int col; // 0:亲水性, 1:疏水性 double rad; // 极角 // 叉积运算,用于判断向量方向 int operator ^ (Vector B){ return x*B.y-y*B.x; } }; Point p1[maxn],p2[maxn]; // p1:原始点, p2:转换后的点 int n; // 判断向量b是否在向量a的左侧(包括共线) bool toLeftTest(Point a,Point b){ return (a^b)>=0; } // 按极角排序 bool cmp(Point a,Point b){ return a.rad<b.rad; } // 计算以点m为隔板上的点时的最大溶解粒子数 int solve(int m){ int ans=0,k=0; // 转换其他点的坐标 for(int i=0;i<n;i++){ if(i==m) continue; // 跳过隔板上的点 p2[k].x=p1[i].x-p1[m].x; p2[k].y=p1[i].y-p1[m].y; if(p1[i].col){ // 疏水性粒子:坐标取反 p2[k].x=-p2[k].x,p2[k].y=-p2[k].y; } p2[k].rad=atan2(p2[k].y,p2[k].x); // 计算极角 k++; } sort(p2,p2+k,cmp); // 按极角排序 // 双指针统计半平面内的最大点数 int l=0,r=0,cnt=2; // cnt初始为2(隔板点+初始粒子) while(l<k){ if(r==l) r=(r+1)%k,cnt++; // 避免l和r重合 while(r!=l && toLeftTest(p2[l],p2[r])){ r=(r+1)%k; cnt++; } cnt--; // 移除左指针指向的粒子 l++; ans=max(ans,cnt); } return ans; } int main() { ios::sync_with_stdio(0),cin.tie(0),cout.tie(0); while(cin>>n){ if(!n) break; for(int i=0;i<n;i++) cin>>p1[i].x>>p1[i].y>>p1[i].col; if(n<=3){ // 特判:点数不超过3时直接全部溶解 cout<<n<<endl; continue; } int ans=0; // 枚举每个点作为隔板上的点 for(int i=0;i<n;i++){ ans=max(solve(i),ans); } cout<<ans<<endl; } return 0; }
四、代码解释
-
结构体定义:
Point
结构体存储粒子坐标、类型和极角,重载叉积运算符用于方向判断。
-
预处理与转换:
- 枚举每个粒子作为隔板上的点,将其他粒子坐标转换为相对于该点的坐标;
- 对疏水性粒子取反,使其与亲水性粒子的判断规则统一。
-
极角排序与双指针:
- 计算所有转换后粒子的极角并排序;
- 使用双指针法统计在某个半平面内的最大粒子数,即溶解的最大数量。
-
主函数处理:
- 输入处理与特判(n≤3时直接输出n);
- 枚举所有可能的隔板位置,取最大值。
五、示例解析
输入:
3 0 0 0 0 1 0 2 2 1
-
枚举隔板点:
- 若隔板经过点(0,0),转换其他点坐标并取反后,计算极角并排序;
- 双指针统计半平面内最大点数,得到结果3。
-
输出:
- 最大溶解粒子数为3。
六、复杂度分析
- 时间复杂度:O(n² log n),其中n为粒子数。枚举每个点作为隔板需要O(n),每次排序需要O(n log n)。
- 空间复杂度:O(n),主要存储转换后的粒子坐标。
七、关键技巧
- 坐标转换:通过取反操作统一亲水性和疏水性粒子的判断规则;
- 极角排序与双指针:高效统计半平面内的最大点数;
- 叉积运算:快速判断向量方向,避免浮点数误差。
- 1
信息
- ID
- 1281
- 时间
- 2000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 2
- 已通过
- 1
- 上传者