CCF-CSP认证考试准备第二天


### Day2:1.202006-1 2.202009-1 3.202012-1 4.202209-1 5.202212-1

#### 1.202006-1:线性分类器(计算几何,小模拟)
(1)题目:
判断给定的直线能否将所有的 A 类点和 B 类点分隔在平面上的不同区域。判断的方法是:
1. 对于每条查询直线,计算每个点在直线上的位置。
2. 如果所有 A 类点和 B 类点分别位于直线的两侧(即符号不同),则输出 `Yes`;否则输出 `No`。

(2)知识点:
存储一堆点的二维坐标时,常见的选择是使用如下几种数据结构:
1.**简单场景(常用)**:如果只是简单存储一组点,推荐使用 `vector<pair<int, int>>`。
- **通过索引访问**:`points[i].first` 和 `points[i].second`。
- **通过范围循环访问**:`for (const auto& point : points) {...}`。
- **通过迭代器访问**:`it->first` 和 `it->second`。
2.**有额外属性**:如果需要存储额外信息,推荐使用自定义结构体 `Point(可推广)` 或者 `map<pair<int, int>, ValueType>`。
3.**需要排序或去重**:使用 set<pair<int, int>>,如果不需要顺序但需要去重,则使用 `unordered_set`。

(3)代码(100):
```
#include <bits/stdc++.h>

using namespace std;

int main(){
    int n,m;
    cin>>n>>m;
    vector<pair<int,int>> va;
    vector<pair<int,int>> vb;
    for(int i=0;i<n;i++){
        int a,b;
        char c;
        cin>>a>>b>>c;
        if(c=='A'){
            va.push_back({a,b});
        }
        else{
            vb.push_back({a,b});
        }
    }
    for(int i=0;i<m;i++){
        int t0,t1,t2;
        cin>>t0>>t1>>t2;
        if(t1==0 && t2==0){
            cout<<"No"<<endl;
            continue;
        }
        bool tmp1;
        bool tmp2;
        bool isExit1=false;
        bool isExit2=false;
        for(int i=0;i<va.size();i++){
            int x=va[i].first;
            int y=va[i].second;
            int sum=t0+t1*x+t2*y;
            if(i==0){                
                if(sum<0){
                    tmp1=false;
                }
                else{
                    tmp1=true;
                }
            }
            else{                
                bool tmp;
                if(sum<0){
                    tmp=false;
                }
                else{
                    tmp=true;
                }
                if(tmp1!=tmp){
                    cout<<"No"<<endl;
                    isExit1=true;
                    break;
                }
            }
        }
        if(isExit1){
            continue;
        }
        else{
            for(int i=0;i<vb.size();i++){
                int x=vb[i].first;
                int y=vb[i].second;
                int sum=t0+t1*x+t2*y;
                if(sum<0){
                    tmp2=false;
                }
                else{
                    tmp2=true;
                }                
                if(tmp1==tmp2){
                    cout<<"No"<<endl;
                    isExit2=true;
                    break;
                }                
            }                                        
        }
        if(isExit2){
            continue;
        }
        else{
            cout<<"Yes"<<endl;
        }        
    }
    return 0;
}
```

(4)改进:
- **简化了循环结构**:将判断是否所有点都在同一侧的逻辑整合到一个循环中,减少了代码冗余。
- **合并条件判断**:通过合并相同逻辑的条件判断,简化了代码结构。
- **更直观的判断方法**:通过直接对比每个点的符号来决定是否输出 "Yes" 或 "No",代码变得更易读。
```
for (int i = 0; i < m; i++) {
    int t0, t1, t2;
    cin >> t0 >> t1 >> t2;
    if (t1 == 0 && t2 == 0) {
        cout << "No" << endl;
        continue;
    }
    //以下简化判断逻辑
    bool sideA = (t0 + t1 * va[0].first + t2 * va[0].second) > 0;
    bool isSeparated = true;
    for (auto& p : va) {
        int sum = t0 + t1 * p.first + t2 * p.second;
        if ((sum > 0) != sideA) { //同样的条件比较
            isSeparated = false;
            break;
        }
    }
    if (isSeparated) {
        for (auto& p : vb) {
            int sum = t0 + t1 * p.first + t2 * p.second;
            if ((sum > 0) == sideA) {
                isSeparated = false;
                break;
            }
        }
    }
    cout << (isSeparated ? "Yes" : "No") << endl;//判断输出
}
```

#### 2.202009-1:称检测点查询(部分排序,小模拟)
(1)题目:找出最近的三个检测点
(2)代码(100):
```
#include <bits/stdc++.h>

using namespace std;

struct Point{
    int id;
    int x;
    int y;
};
int X,Y;

bool cmp(const Point& a,const Point& b){
    int d1=(X-a.x)*(X-a.x)+(Y-a.y)*(Y-a.y);
    int d2=(X-b.x)*(X-b.x)+(Y-b.y)*(Y-b.y);
    if(d1==d2){
        return a.id<b.id;
    }
    else{
        return d1<d2;
    }
}

int main(){
    int n;
    cin>>n>>X>>Y;
    vector<Point> v(n);
    for(int i=0;i<n;i++){
        v[i].id=i+1;
        cin>>v[i].x>>v[i].y;
    }
    sort(v.begin(),v.end(),cmp);
    cout<<v[0].id<<endl<<v[1].id<<endl<<v[2].id<<endl; 
    return 0;
}
```
(3)改进:
1.可以在输入时直接计算出每个检测点的距离平方,并将其存储在结构体中,然后在比较时直接使用存储的距离。
struct Point{ 
    int id; 
    int x; 
    int y; 
    int dist; // 直接存储距离的平方
 }; 
 bool cmp(const Point& a, const Point& b){ 
     if (a.dist == b.dist){ 
         return a.id < b.id; 
    } 
    return a.dist < b.dist; 
}

2.部分排序(nth_element):如果只需要找出最近的三个检测点,可以使用 `nth_element` 来代替 `sort`。`nth_element` 可以在 O(n) 的时间复杂度内找到第 k 小的元素,并将它们放在容器的前 k 个位置,不需要完全排序。
// 使用 nth_element 找到前三个最近的点 
nth_element(points.begin(), points.begin() + 3, points.end(), cmp); 
// 将前3个元素排序,得到最近的3个点 
sort(points.begin(), points.begin() + 3, cmp);
[[排序#^9b53cb|nth_element部分排序]]

3.[CSP202009-1 称检测点查询(100分)【数学】_csp 202009-1-CSDN博客](https://blog.csdn.net/tigerisland45/article/details/110773636?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522172433720516800185883492%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=172433720516800185883492&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~hot_rank-22-110773636-null-null.142^v100^pc_search_result_base3&utm_term=202009-1%3A%E7%A7%B0%E6%A3%80%E6%B5%8B%E7%82%B9%E6%9F%A5%E8%AF%A2&spm=1018.2226.3001.4187)
对前3个点部分排序(手动排序,小规模)

#### 3.202012-1:期末预测之安全指数(小模拟)
简单,过
最后输出可以改为cout<<(sum>0?sum:0)或者cout<<max(0LL,sum)(**注意:sum为long long型,前面要改成0LL,不能写0**);

#### 4.202209-1:如此编码(小模拟,数学)
数组存储加数学计算
(1)代码(100):
```
#include <bits/stdc++.h>

using namespace std;

int main(){
    int n,m;
    cin>>n>>m;
    vector<int> va(n+1);
    vector<long long> vc(n+1,1);
    vector<int> vb(n+1);
    for(int i=1;i<n+1;i++){
        cin>>va[i];
    }
    for(int i=1;i<n+1;i++){
        for(int j=1;j<=i;j++){
            vc[i]*=va[j];
        }
    }
    for(int i=1;i<n+1;i++){
        vb[i]=(m%vc[i]-m%vc[i-1])/vc[i-1];
    }
    for(int i=1;i<n+1;i++){
        cout<<vb[i]<<" ";
    }
    return 0;
}
```
(2)优化:
**前缀乘积的计算**:ci=a1×a2×⋯×ai
在计算 `vc[i]` 时使用了嵌套的循环,这会导致每个 `vc[i]` 都重新计算前面的乘积,时间复杂度为 O(n^2)。实际上,`vc[i]` 可以通过 `vc[i-1]` 直接计算,时间复杂度为 O(n)。
// 优化前缀乘积的计算 
for(int i=1;i<n+1;i++){
    cin>>va[i];
    vc[i]=vc[i-1]*va[i];//前提保证vc[0]=1
}
**数论**中有关进制的概念(较难):
- 在十进制中,一个数可以表示为 `个位数 + 十位数 * 10 + 百位数 * 100`,等等。类似地,编码公式中的每一项 `b_i * vc[i-1]` 类似于进制系统中的“位”,`vc[i-1]` 类似于基数。
- - **基数(Radix)**:决定了每个数位的取值范围和权重,常见的基数有二进制(2)、十进制(10)、十六进制(16)等。
- **位(Digit)**:是表示数字的基本单位,每个位在不同的基数下有不同的可能取值。
- **在这段代码中的体现**:`m` 可以看作一个“多位数”,其中每一位的基数由前缀乘积 `vc[i-1]` 决定。我们通过**除法提取每一位,然后通过模运算去掉已经提取的部分**。这种方式与我们**将一个十进制数逐位提取出各位的数字是相似的**。
for (int i = n; i >= 1; i--) {
    vb[i] = m / vc[i];
    m = m % vc[i];
}

#### 5.202212-1:现值计算(小模拟,格式化输出)
(1)题目理解(出现一些偏差):基于上述分析,我们使用如下的模型来衡量时间价值:假设银行的年利率为 i,当前(第 0 年)的 x 元就等价于第 k 年的 x*(1+i)^k 元;相应的,第 k 年的 x 元的当前价值实际为 x*(1+i)^(−k) 元。**遍历k应该使用第二句话**
**pow函数的使用**
(2)代码(100):
```
#include <bits/stdc++.h>

using namespace std;

int main(){
    int n;
    double i;
    cin>>n>>i;
    double res=0;
    for(int k=0;k<=n;k++){
        int x;
        cin>>x;
        res+=x*pow(1+i,-k);
    }
    printf("%lf",res);
    return 0;
}
```

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值