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


### Day6: 1.202006-2 2.202009-2 3.202012-2

#### 1.202006-2:稀疏向量(map哈希,双指针(但一开始未用到))
(1)原来想两个数组暴力,但只能得60分,因为数组要存储连续数据,但用map能得100分,因为map用于存储稀疏数据比较有效
```
//1.671s
#include <bits/stdc++.h>

using namespace std;

int main(){
    int n,a,b;
    cin>>n>>a>>b;
    long long res=0;
    map<int,int> mpa;
    map<int,int> mpb;
    for(int i=0;i<a;i++){
        int x,y;
        cin>>x>>y;
        mpa[x]=y;
    }
    for(int i=0;i<b;i++){
        int x,y;
        cin>>x>>y;
        mpb[x]=y;
    }
    if(mpa.size()>mpb.size()){
        swap(mpa,mpb);
    }
    for(auto it=mpa.begin();it!=mpa.end();it++){
        int x=it->first;
        if(mpb.find(x)!=mpb.end()){
            res+=mpa[x]*mpb[x];
        }
    }
    cout<<res;
    return 0;
}
```
(2)优化:
1.不需要两个map,只需要一个map存储a,然后遍历b的时候计算res即可
2.以后刷题尽量用scanf和printf,更快
```
//1.015s
#include <bits/stdc++.h>

using namespace std;

int main(){
    int n,a,b,x,y;
    scanf("%d%d%d",&n,&a,&b);
    long long res=0;
    map<int,int> mpa;
    for(int i=0;i<a;i++){
        scanf("%d%d",&x,&y);
        mpa[x]=y;
    }
    for(int i=0;i<b;i++){
        scanf("%d%d",&x,&y);
        res+=mpa[x]*y;//如果访问一个不存在的键,`map` 会自动创建该键,并将其值初始化为默认值(对于 `int` 类型,默认值为 `0`)。
    }
    printf("%lld",res);
    return 0;
}
```
(2)学习**双指针**(直观):
遍历两个**有序**列表(在本题中是稀疏向量的非零元素列表)时**减少不必要的比较和迭代**。
```
//921ms
#include <bits/stdc++.h>

using namespace std;

int main(){
    int n,a,b;
    scanf("%d%d%d",&n,&a,&b);
    vector<pair<int,int>> va(a);
    vector<pair<int,int>> vb(b);
    for(int i=0;i<a;i++){
        scanf("%d%d",&va[i].first,&va[i].second);
    }
    for(int i=0;i<b;i++){
        scanf("%d%d",&vb[i].first,&vb[i].second);
    }
    long long res=0;
    int i=0,j=0;
    while(i<a && j<b){
        if(va[i].first==vb[j].first){
            res+=va[i].second*vb[j].second;
            i++;
            j++;
        }
        else if(va[i].first<vb[j].first){
            i++;
        }
        else{
            j++;
        }
    }
    printf("%lld",res);
    return 0;
}
```
**双指针方法**在特定情况下比使用 `map` 方法快得多,尤其是当两个向量的非零元素按顺序排列时。这种方法不仅降低了时间复杂度,还能最大化地利用现代处理器的缓存架构。

#### 2.202009-2:风险人群筛查(小模拟)
简单,过(100)
```
#include <bits/stdc++.h>

using namespace std;

int main(){
    int n,k,t,xl,yd,xr,yu,x,y;
    scanf("%d %d %d %d %d %d %d",&n,&k,&t,&xl,&yd,&xr,&yu);
    int pass=0,stay=0;
    while(n--){
        int staycnt=0;
        bool isPass=false;
        bool isStay=false;
        for(int i=0;i<t;i++){
            scanf("%d %d",&x,&y);
            if(x>=xl && x<=xr && y>=yd && y<=yu){
                isPass=true;
                staycnt++;
            }
            else{
                staycnt=0;
            }
            if(staycnt>=k){
                isStay=true;//注意下面不能加break,不然数据输入对应有问题
            }
        }
        if(isPass){
            pass++;
        }
        if(isStay){
            stay++;
        }
    }
    printf("%d\n%d",pass,stay);
    return 0;
}
```

#### 3.202012-2:期末预测之最佳阈值(排序+前缀和)
(1)自己做50分
(2)学习:
##### 1.可读性好的结构体 [CCF CSP认证 2020-12-2 期末预测之最佳阈值 题解及满分代码(C++11)](https://blog.csdn.net/weixin_49070253/article/details/123202976?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522172468561016800211539993%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fvipall.%2522%257D&request_id=172468561016800211539993&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~vipall~first_rank_ecpm_v1~hot_rank-10-123202976-null-null&utm_term=202012-2%EF%BC%9A%E6%9C%9F%E6%9C%AB%E9%A2%84%E6%B5%8B%E4%B9%8B%E6%9C%80%E4%BD%B3%E9%98%88%E5%80%BC&spm=1018.2226.3001.4187)
**关键题目理解**:对于每一个阈值t,所有 y<t 的学生的result 若为0,则算预测正确;所有y>=t 的学生的result 若为1,则算预测正确。
因此,求阈值的预测正确次数就变成了分别统计y<t 的学生中result=0 的个数和y>=t 的学生中result=1 的个数,再相加。
因为需要分别统计y<t 和y>=t 的情况,因此我们可以在计算之前先将结构体数组**按y递增排序**,这样可以从小到大选取阈值,并且方便统计以 y 为界的左右两边的情况。
**前缀和优化**:我们完全可以运用类似前缀和的方法,在选取阈值之前,通过一次正向和一次逆向遍历数组,计算出对每一个元素 stu[i], stu[1]到stu[i-1]中 result=0 的个数和 stu[i]到stu[m]中result=1 的个数。(**注意**:前缀和和前缀积第0号元素的值要注意,不行可以自主添加)
**满分代码**(自己理解手写一遍,跟网站里面的代码有稍许不一样):
```
#include <bits/stdc++.h>

using namespace std;

struct Student{
    int y,res;
    int before,after;
};

bool cmp(const Student& a,const Student& b){
    if(a.y==b.y)    return a.res<b.res;
    return a.y<b.y;
}
int main(){
    int m;
    scanf("%d",&m);
    vector<Student> v(m+1);
    //添加第一个元素,方便前缀和计算,边界不用考虑 
    v[0].y=-1;
    v[0].res=1;
    v[0].before=0;
    for(int i=1;i<=m;i++){
        scanf("%d%d",&v[i].y,&v[i].res);
        v[i].before=0;
        v[i].after=0;        
    }
    sort(v.begin(),v.end(),cmp);
    //前缀和计算前面res为0的个数
    for(int i=1;i<=m;i++){
        v[i].before=v[i-1].before+(v[i-1].res==0);
    }
    //类似于前缀和计算后边(包括自己)res为1的个数
    if(v[m].res==1){
        v[m].after=1;
    }
    for(int i=m-1;i>=1;i--){//注意边界,因此之前要先判断v[m].after
        v[i].after=v[i+1].after+(v[i].res==1);
    }
    int besty=0,maxcnt=-1;
    for(int i=1;i<=m;i++){
        int cnt=v[i].before+v[i].after;
        if(cnt>=maxcnt){
            besty=v[i].y;
            maxcnt=cnt;
        }
        while(v[i].y==v[i+1].y && i<m){
            //注意同一个y只要计算一次
            i++;
        }
    }
    printf("%d",besty);
    return 0;
}
```

##### 2.效率和简洁性的二维数组 [AcWing 3298. 期末预测之最佳阈值 - AcWing](https://www.acwing.com/solution/content/101956/)
**关键**:a[0][i]和a[1][i]分别记录前i个数(不包括自己)中0和1的个数 
**注意**:const int N=100010初始化不要错了,一开始写的10010小了之后要不断分配空间会超时
```
#include <bits/stdc++.h>

using namespace std;

const int N=100010;
int a[2][N];//a[0][i]和a[1][i]分别记录前i个数(不包括自己)中0和1的个数 

int main(){
    int m;
    scanf("%d",&m);
    vector<pair<int,int>> v(m+1);
    v[0].first=-1;
    v[0].second=-2;
    for(int i=1;i<=m;i++){
        scanf("%d%d",&v[i].first,&v[i].second);
    }
    sort(v.begin()+1,v.end());
    for(int i=1;i<=m;i++){
        //前缀和
        a[0][i]=a[0][i-1]+(v[i-1].second==0);
        a[1][i]=a[1][i-1]+(v[i-1].second==1);
    }
    int besty=0,maxcnt=0;
    for(int i=1;i<=m;i++){
        //使用前缀和,记得补最后的条件
        int cnt=a[0][i]+a[1][m]-a[1][i]+(v[m].second==1);
        if(cnt>=maxcnt){
            besty=v[i].first;
            maxcnt=cnt;
        }
        while(i<m && v[i].first==v[i+1].first){
            i++;
        }
    }
    printf("%d",besty);
    return 0;
}
```


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值