CCF-CSP 202006 赛题训练

Posted ZSYL

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CCF-CSP 202006 赛题训练相关的知识,希望对你有一定的参考价值。

【CCF CSP-20200601】线性分类器

题意概述

给出 n 个二维平面上的点,这些点可以分成 A、B 两类。给出 m 条直线,针对每条直线判断是否能将 A、B 两类点完全分隔开。

输入输出格式

输入共 n + m + 1 行。第一行包含用空格分隔的两个正整数 n 和 m,分别表示点和查询的个数。接下来 n 行,每行依次按横坐标 纵坐标 类别的格式输入 n 个点的信息,其中坐标为整数,类别为一个大写英文字母 A 或 B。接下来 m 行,每行依次输入 m 条直线的信息,表示给定直线一般式 A+Bx+Cy=0 的三个参数 A、B、C。

输出共 m 行,每行输出一个字符串。第 j 行 ( 1 ≤ j ≤ m ) (1\\le j\\le m) 1jm输出的字符串对应第 j 条直线的查询结果:如果给定直线可以完美分隔 A、B 两类点,则输出 Yes;否则输出 No。

数据规模

0 < n ≤ 10 3 , 0 < m ≤ 20 0<n \\le 10^3,0<m \\le 20 0<n103,0<m20

算法设计

如何判断两个点是否在直线的一侧呢?这里要用到一些简单的数学知识。对于一条直线 A x + B y + C = 0 Ax+By+C=0 Ax+By+C=0,如果点 ( x 0 , y 0 ) \\left(x_0,y_0\\right) (x0,y0)满足 A x 0 + B y 0 + C > 0 Ax_0+By_0+C>0 Ax0+By0+C>0,则点 ( x 0 , y 0 ) \\left(x_0,y_0\\right) (x0,y0)在该直线上方;如果点 ( x 0 , y 0 ) \\left(x_0,y_0\\right) (x0,y0)满足 A x 0 + B y 0 + C < 0 Ax_0+By_0+C<0 Ax0+By0+C<0,则点 ( x 0 , y 0 ) \\left(x_0,y_0\\right) (x0,y0)在该直线下方;如果点 ( x 0 , y 0 ) \\left(x_0,y_0\\right) (x0,y0)满足 A x 0 + B y 0 + C = 0 Ax_0+By_0+C=0 Ax0+By0+C=0,则点 ( x 0 , y 0 ) \\left(x_0,y_0\\right) (x0,y0)在该直线上。

由于题目保证不存在恰好落在直线上的点,因此我们无需考虑点落在直线上的情况。因此,如果两个点都落在直线上方或直线下方,那么这两个点一定在直线的一侧;反之,如果一个点落在直线上方,一个点落在直线下方,那么这两个点一定不在直线的一侧。

由于输入的点最多只有 1000 个,输入的直线最多也只有 20 条,因此我们可以针对输入的每条直线,暴力查找每一个同类的点是否位于该直线的一侧。算法的时间复杂度为 O ( m n ) O\\left(mn\\right) O(mn)

C++代码

#include <bits/stdc++.h>
using namespace std;
using gg = long long;
int main() 
    ios::sync_with_stdio(false);
    cin.tie(0);
    gg ni, mi;
    cin >> ni >> mi;
    vector<vector<array<gg, 2>>> points(2);  // array<int,2>是指一个由两个整数组成的数组,即int类型的数组,其中的元素个数为2。
    gg xi, yi, ai, bi, ci;
    string typei;
    while (ni--) 
        cin >> xi >> yi >> typei;
        points[typei[0] - 'A'].push_back(xi, yi);
    
    while (mi--) 
        cin >> ai >> bi >> ci;
        for (auto& p : points) 
            for (gg i = 1; i < p.size(); ++i) 
                if ((ai + bi * p[i][0] + ci * p[i][1] > 0) ^ (ai + bi * p[0][0] + ci * p[0][1] > 0)) 
                    cout << "No\\n";
                    goto loop;
                
            
        
        cout << "Yes\\n";
    loop:;
    
    return 0;

将点的坐标代入直线方程,通过计算结果大于0还是小于0,判断点在直线的哪一侧。

#include <cstdio>
#include<iostream>
using namespace std;

int n, m;
int c0, c1, c2;

struct Node 
    int x, y;
    char type;
node[1005];

int main() 
    cin >> n >> m;
    for (int i = 0; i < n; i++) 
        cin >> node[i].x >> node[i].y;
        getchar();
        node[i].type = getchar();
    
    // 判断二分类
    for (int i = 0; i < m; i++) 
        cin >> c0 >> c1 >> c2;
        int succ = 1;
        int flagA = -1, flagB = -1;
        for (int j = 0; j < n; j++) 
            int t = (c0+c1*node[j].x + c2*node[j].y > 0) ? 1 : 0;  //
            if (node[j].type == 'A') 
                if (flagA == -1) 
                    flagA = t;  // 出现在一侧
                else if (flagA != t) 
                    succ = 0;
                    break;
                
             else if (node[j].type == 'B') 
                if (flagB == -1)
                    flagB = t;
                else if (flagB != t) 
                    succ = 0;break;
                
            
            if (flagA == flagB) 
                succ = 0; break;  // 不能在同一侧
            
        
        string s = succ ? "Yes" : "No";
        cout << s << endl;
    
    return 0;

代码来源

【CCF CSP-20200602】稀疏向量

题意概述

给出两个 n 维向量的稀疏向量表示,计算这两个向量的内积。所谓稀疏向量表示,就是指用(index value)的格式(索引由 1 开始)表示一个向量在 index 维度上的值为 value,例如向量 v ⃗ = ( 0 , 0 , 0 , 5 , 0 , 0 , − 3 , 0 , 0 , 1 ) \\vecv=\\left(0,0,0,5,0,0,-3,0,0,1\\right) v =(0,0,0,5,0,0,3,0,0,1),就可以表示成 v ⃗ = [ ( 4 , 5 ) , ( 7 , − 3 ) , ( 10 , 1 ) ] \\vecv=\\left[\\left(4,5\\right),\\left(7,-3\\right),\\left(10,1\\right)\\right] v =[(4,5),(7,3),(10,1)]

输入输出格式

输入的第一行包含用空格分隔的三个正整数 n、a 和 b,其中 n 表示向量 u 和 v 的维数,a 和 b 分别表示两个向量所含非零值的个数。接下来 a 行,每行按index value的格式输入向量 u 的稀疏表示。接下来 b 行,每行按index value的格式输入向量 v 的稀疏表示。注意 index 由 1 开始。

输出一个整数,表示向量 u 和向量 v 的内积。

数据规模

n ≤ 10 9 , 0 < a , b < n , 0 < a , b ≤ 5 × 10 5 n\\le 10^9,0<a,b<n,0<a,b\\le 5 \\times10^5 n109,0<a,b<n,0<a,b5×105

算法设计

由于 n 最大为 10 9 10^9 109,无法直接开辟一个数组。可以先用 unordered_map 将向量 u 的索引和值对应存储起来。读取向量 v 的稀疏表示时,直接将其与向量 u 对应索引位置的值的乘积加和,最后输出结果即可。

10 3 4
4 5
7 -3
10 1
1 10
4 20
5 30
7 40

-20

样例解释

u = (0, 0, 0, 5, 0, 0, -3, 0, 0, 1)
v = (10, 0, 0, 20, 30, 0, 40, 0,0, 0)
u · v = 5 × 20 + (-3) × 40 = 20

C++代码

#include<bits/stdc++.h>
using namespace std;

int n;
int a, b;
long long ans;  // 使用long long

vector<pair<int, int>> u, v;

int main() 
    cin >> n >> a >> b;
    int id, val;
    for (int i = 0; i < a; i++) 
        cin >> id >> val;
        u.push_back(id, val);
    
    for (int i = 0; i < b; i++) 
        cin >> id >> val;
        v.push_back(id, val);
    
    int i = 0, j = 0;
    // 类似归并排序的内味儿,总有一个先到末尾
    while (i < a && j < b) 
        if (u[i].first == v[j].first) 
            ans += u[i].second * v[j].second;
            i++;
            j++;
         else以上是关于CCF-CSP 202006 赛题训练的主要内容,如果未能解决你的问题,请参考以下文章

CCF-CSP 201604 赛题训练

CCF-CSP 201612 赛题训练

CCF-CSP 201609 赛题训练

CCF-CSP 201709 赛题训练

CCF-CSP 202203 赛题训练

CCF-CSP 201909 赛题训练