CSP19届
第1题 线性分类器
题干大意:在一个直角坐标平面中,有两种“点”:A 和 B,A、B点都有多个。现在给定一个直线方程,判断所有的A和所有的B点是否被这条直线分隔开了。如果是,输出Yes,否则输出No。
为了更好理解题意,我画了下面的坐标图
如下图,假定(0,3)、(0,1)属于A类点,(1,0)、(2,1)、(3,0)属于B类点。图中还有三条直线,一条是x+y-2=0,一条是x-y=0,一条是x+y-4=0。

分析1:
看得出,x-y=0这条直线把A类点:(0,3)、(0,1)分隔在线的一边,把B类点(1,0)、(2,1)、(3,0)分隔在线的另一边。
而x+y-2=0 这条线却没有成功分隔,因为在A类的点中,(0,3)在x+y-2的上方,(0,1)在x+y-2的下方。在B类点中,(1,0)在x+y-2=0的下方,(2,1)、(3,0)在B类点的下方。
x+y-4=0这条线也没有成功分隔,因为A类点(0,3)、(0,1)在 x+y -4 =0 的下方,B类点(1,0)、(2,1)、(3,0)也在 x+y-4=0的下方。A类点和B类点是同侧了,这不符合题意要求:A类的点和B类的点被直线分隔
分析2:
看是看得出来,可是怎么用数学表示呢?也就是怎么通过数学计算就知道点在直线的哪一侧?答案是可以把点代入直线方程,判断计算结果是大于0还是小于0 就可以知道点在直线哪一侧了。
一般直线方程如下
f(x,y) = a + bx +cy
点(x0,y0)代入 f(x,y) = a + bx +cy 得 a + bx0 + cy0 。这个式子的计算结果的符号有三种可能。
正数:f(x0,y0) = a + bx0 + cy0 > 0
0:f(x0,y0) = a + bx0 + cy0 = 0
负数:f(x0,y0) = a + bx0 + cy0 < 0
对于一个具体的点,代入f(x,y) 只有一种可能,要么是正,要么负,要么等于0。注意,等于0表示点在直线上。
到此,已经知道怎么通过数学公式计算来判断一个点在直线哪一侧。
算法的思路:
把A类的点一个一个代入直线方程,如果A类的点都是在直线的一侧,它们计算结果的符号一定是相同的。如果有一个A类的点代入直线方程,它计算结果的符号与前面算的符号不同,就知道,这条直线没有把A类点分隔开,此时可以直接输出No,不必判断B类点是不是都在直线的另一侧。
如果遍历完A类点,计算的符号都相同,就知道A类点在直线一侧,用变量flagA 来记录A类点的符号。继续判断B类的点
用同样的方式,把B类的点一个一个代入直线方程,如果B类的点都是在直线的一侧,它们计算结果的符号一定是相同的。如果有一个B类的点代入直线方程,它计算结果的符号与前面算的符号不同,就知道,这条直线没有把B类点分隔开,此时也直接输出No。
遍历完B类点,计算的符号都相同,就知道b类点在直线一侧,用变量flagb 来记录B类点的符号。
到此,知道了A类点和B类点都在直线一侧。但是还有一个问题,A类点可能与B类同侧,因此最后要判断A类点的符号与B类点的符号必须不同,才输出Yes,否则说明A类点和B类的点是同一侧输出No。
计算演示:
这里采用上面的坐标图的点和直线数据来实现算法思路。
规定一下判断的直线的顺序:
(1)x+y-2=0
(2) x-y=0
(3) x+y-4=0
已知A类点:(0,3)、(0,1)
已知B类点:(1,0)、(2,1)、(3,0)
(1) x+y-2=0
把(0,3)代入 x+y-2=0,得 0+3-2=1>0,标记(0,3)为1 (1表示正号,-1表示符号)
把(0,1)代入 x+y-2=0,得 0+1-2 =-1<0,标记(0,1)为-1,与前面计算的点的符号进行判断,1 != -1 ,所以,直接跳出判断,不用计算B类点,直接输出No
(2) x-y=0
对A类点
把(0,3)代入 x-y=0,得 0-3=-3<0,标记(0,3)为-1 (1表示正号,-1表示负号)
把(0,1)代入 x-y=0,得 0-1=-1<0,标记(0,1)为-1,与前面计算的点的符号进行判断,-1 == -1 ,符号相同。
A类点都判断完,都在直线的一侧,可以继续判断B类点
对B类点
把(1,0)代入x-y=0,得 1-0=1>0, 标记(1,0)为1,
把(2,1)代入x-y=0,得2-1=1>0,标记(2,1)为1,与前面的点的符号判断,1==1,符号相同,继续判断下一个点
把(3,0)代入x-y=0,得3-0=3>0,标记(3,0)为1,与前面的点的符号判断,1==1,符号相同。
B类点判断完了,都在直线的一侧,最后判断A类点和B类点是不是同侧。
-1 != 1,可知A B 点不同侧。输出Yes。
(3) x+y-4=0
对A类点
把(0,3)代入 x+y-4=0,得 0+3-4=-1<0,标记(0,3)为-1 (1表示正号,-1表示负号)
把(0,1)代入 x+y-4=0,得 0+1-4=-3<0,标记(0,1)为-1,与前面计算的点的符号进行判断,-1 == -1 ,符号相同。
A类点都判断完,都在直线的一侧,可以继续判断B类点
对B类点
把(1,0)代入x+y-4=0,得 1+0-4=-3<0, 标记(1,0)为-1,
把(2,1)代入x+y-4=0,得2+1-4=-1<0,标记(2,1)为-1,与前面的点的符号判断,-1==-1,符号相同,继续判断下一个点
把(3,0)代入x+y-4=0,得3+0-4=-1<0,标记(3,0)为-1,与前面的点的符号判断,-1==-1,符号相同。
B类点判断完了,都在直线的一侧,最后判断A类点和B类点是不是同侧。
-1 == -1,可知A B 点同侧。输出No。
代码实现
1 #include<stdio.h> 2 struct node { 3 long long x,y; 4 }A[20000],B[20000]; 5 int main(){ 6 int n,m; 7 int x,y; 8 char typ; 9 long long a,b,c; 10 int i,j; 11 scanf("%d %d",&n,&m); 12 int posA=0,posB=0; 13 for(i=0;i<n;i++){ 14 scanf("%d %d %c",&x,&y,&typ); 15 if(typ == 'A'){ 16 A[posA].x=x; 17 A[posA].y=y; 18 posA++; 19 }else { 20 B[posB].x=x; 21 B[posB].y=y; 22 posB++; 23 } 24 } 25 int flagA,flagB; 26 for(i=0;i<m;i++){ 27 scanf("%lld %lld %lld",&a,&b,&c); 28 if(a + A[0].x*b +A[0].y*c <0)flagA=-1; 29 else flagA=1; 30 for(j=1;j<posA;j++){ 31 if(a + A[j].x*b +A[j].y*c <0){ 32 if(1 + flagA !=0)break; 33 }else { 34 if(-1 + flagA !=0)break; 35 } 36 } 37 if(j<posA){ 38 printf("No\n"); 39 }else { 40 if(a + B[0].x*b + B[0].y*c <0)flagB=-1; 41 else flagB = 1; 42 43 if(flagA + flagB == 0){ 44 for(j=1;j<posB;j++){ 45 if(a + B[j].x*b + B[j].y*c <0){ 46 if(-1 + flagA!=0)break; 47 }else { 48 if(1 + flagA!=0)break; 49 } 50 } 51 if(j<posB){ 52 printf("No\n"); 53 }else { 54 printf("Yes\n"); 55 } 56 }else { 57 printf("No\n"); 58 } 59 60 } 61 } 62 return 0; 63 }

浙公网安备 33010602011771号