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 }

 

posted @ 2020-06-30 13:49  ACruning  阅读(72)  评论(0)    收藏  举报