CCF准备日记——202006-1
程序员文章站
2022-04-15 11:20:07
CCF准备日记——202006-1题目编号:202006-1题目名称:线性分类器使用语言:Java(Eclipese)题目描述:我的代码:import java.util.ArrayList;import java.util.Scanner;class point{private int x;private int y;public point() {}public point(int _x,int _y) {this.x = _x;this...
CCF准备日记——202006-1
题目编号:202006-1
题目名称:线性分类器
使用语言:Java(Eclipese)
题目描述:
我的代码:
import java.util.ArrayList;
import java.util.Scanner;
class point{
private int x;
private int y;
public point() {
}
public point(int _x,int _y) {
this.x = _x;
this.y = _y;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
}
public class Main {
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();//点的个数
int m = scanner.nextInt();//待判定的直线个数
ArrayList pointsA = new ArrayList(); //将类别为A的单独放置
ArrayList pointsB = new ArrayList();//将类别为B的单独放置
int tx = 0;//临时变量
int ty = 0;//临时变量
int tz = 0;//临时变量
char tlabel = 'd';//临时变量
for(int i=0;i<n;i++) {
tx = scanner.nextInt();
ty = scanner.nextInt();
tlabel = scanner.next().charAt(0);
if(tlabel == 'A') {
pointsA.add(new point(tx,ty));
}else {
pointsB.add(new point(tx,ty));
}
}
//System.out.println(pointsA.size()+";"+pointsB.size());
int lines[][] = new int[m][4];//lines[0][3]=1表示第一条直线线性可分,0表示线性不可分
for(int j=0;j<m;j++) {
tx = scanner.nextInt();
ty = scanner.nextInt();
tz = scanner.nextInt();
lines[j][0] = tx;
lines[j][1] = ty;
lines[j][2] = tz;
}
int numA = 0;//统计A分类正确或错误的个数
int numB = 0;//统计B分类正确或错误的个数
for(int i=0;i<m;i++) {
numA = 0;
numB = 0;
for(int j=0;j<pointsA.size();j++) {
if((lines[i][0]+lines[i][1]*((point) pointsA.get(j)).getX()+lines[i][2]*((point) pointsA.get(j)).getY())>0)
numA++;
}
for(int k=0;k<pointsB.size();k++) {
if((lines[i][0]+lines[i][1]*((point) pointsB.get(k)).getX()+lines[i][2]*((point) pointsB.get(k)).getY())<0)
numB++;
}
if((numA+numB) == n || (numA+numB) == 0)
//如果numA与numB的和为n或者为0时表示分类正确
lines[i][3] = 1;//1表示可以分类正确
else
lines[i][3] = 0;
}
for(int i=0;i<m;i++) {
if(lines[i][3] == 1) {
System.out.println("Yes");
}else {
System.out.println("No");
}
}
}
}
我的分析:
- 这个问题一看就特别熟悉,也是因为最近在学习机器学习的缘故。看似题目说的很高深,其实代码逻辑很简单,没有涉及到模型的训练只是用来判断直线针对数据点是否线性可分。
- 其实这道题我写了两版,第一版也可以通过题目给出的测试数据,但是放到官方判题系统中得了个0分。仔细一想确实里面有很多漏洞,很多不合理有待改良得地方。
- 先说明一下最初得思路,输入数据点统一存入到一个多维数组中,其分类标签A、B也作为其中一项属性。然后再输入直线数据得循环中去判断处理是否针对所有数据点线性可分,我的判断依据是所有相同类别得点算出来得结果值都要是同号。我又陷入了思考,看似简单得判断逻辑——判断同号,该怎么实现呢?我又想到了相邻同类别得点其成绩大于0就可以判断同号。但我又忽略了两个问题,很有可能某个类别只有1个点,那么就没法去计算与之相邻得结果乘积,所以我又单独拉出来判断只有1个点时怎么处理。还没有完…如果数据过大,很可能两个结果得乘积会越界。总之处处都是漏洞,给0分也是活该!
- 下面是第二版的设计思路,明显代码结构更清晰,思路更严谨。首先我们在输入点时就不用保存其分类信息而是根据分类信息分成两个动态数组存储,有关Java的动态数组我们要用ArrayList实现(具体细节可以参看代码)。分成两部分处理的好处是不用在多重for循环嵌套中又加上if的判断。既然用结果乘积判断是否线性可分漏洞百出,那么我们有了更好的方法。比如A类样本我们就统计结果大于0的个数numA,B类样本我们就统计结果小于0的个数numB。这里有人可能会问,你怎么知道A类结果就是大于0的?没有关系!我们最终的判定条件是numA+numB==n or 0,也就是说如果我们的假设类别正确得到n,假设类别错误得到0。如果得到两个值之外的任何值都是线性不可分的情况。
- 巧妙地思路化解问题,拿下100分!
本文地址:https://blog.csdn.net/dzc_go/article/details/110826083