2023csp-j复赛冲刺模拟赛补题报告

                                                               CSP-J模拟赛 (补题)
                                                                                                     日期 2023年10月2日星期一
                                                                                                                             学号 S05510
比赛概况
总分400分,拿到40分,其中第1题40分,2题0分,3题0分,4题0分,
赛后补题:全部正确
比赛过程
先做第一题,没怎么调试,就去做第二题,结果枚举循环写错了,暴力分没拿到,第三题找了一半的规律,long  long运行错误,第四题没做
 

解题报告:

一.人员借调
1.题目大意

小可在 A 地工作能力非常出众,B 地的领导想借调小可帮忙处理 n 件事情,小可处理第 i 件的耗时为 a​i​​分钟。

正常借调的过程为小可从 A 地到 B 地进行处理,处理结束之后回到 A 地。

但是由于小可能力过于突出,如果小可在 B 地待连续大于等于240分钟时,A 地领导会非常的不高兴,将强制把小可留在 A 地 7 天( 10080 分钟),7 天结束后小可可以继续留在 A 地正常工作或者继续前往 B 地帮忙处理事情。

于是,小可有了一个对策,在 240 分钟快到的时候就此 B 地回到 A 地,然后再去 B 地,这样的话 240 分钟就会重新计时。注意:从 A 地往返一次 B 地会耗时 400 分钟(这个时间不计算到待在 B 地的时间)。

现在小可从 A 地准备出发,需要在 B 地处理完所有事,然后回到 A 地正常的工作。

注意:n 件事情的不可以打乱,并且开始处理一个事情时,这个事情必须处理完才可以继续后面的内容(无论是回 A 地还是继续处理后面的事情)。

请问小可至少需要多少分钟?

2.比赛中的思考
思路对了,就是少加了400,只找了两种情况,少了一种
3.解题思路
从A去B处理事件,统计时间(处理完后要返回A,花费400)
    无论做多少事,最后都要返回A,所以一定有往返花费
    一个事情的完成有两种情况:
        1.所有事件的总时间小于240,呆在B全处理完再返回,则只需计算1次往返
        2.所有事件的总时间超过240,需要在A滞留7天,则有两种选择
            ①全部一次性在B做完再回A滞留,花费=总时间+滞留+往返400
            ②预估到做下一件事情(连续事件)要超过240,则做完此件事回A后再回来做
                        花费=总时间+临时往返次数*400 +往返400 
    因此只需看往返次数*400与滞留时间+400大小即可 
        
4 .AC代码

#include<bits/stdc++.h>
using namespace std;
int main(){
    int n,a[10005];
    unsigned long long sum=0,cnt=0,k=0,p=0,r,ans=0; 
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>r;
        sum+=r;
        ans+=r;
        if(sum>=240){
            cnt++;
            sum=r;
        }
    }
    if(cnt>=1){
        if(n==1){
            ans+=10080;
        }
        else{
            ans=min(ans+10080,ans+cnt*400);
        }    
    }
    cout<<ans+400;
}

二.计算
        1.题目大意
            

计算需要使用到三个整数 m,n,k

计算条件如下(设 x 为满足计算条件的数字):

1:m≤x≤n

2:x 在十进制下所有位上的数字之和为 k

根据计算条件得到的 x 可能会很多,请输出十进制下所有位上的数字的积最大的那个(如果有多个积相等,则输出 x 最小的那个)

数据保证有解。


        2.比赛中的思考
用枚举,样例没测出来
         3.解题思路
打表预处理再进行判断找最大,复杂度O(n)
          4.AC代码

                        
#include<bits/stdc++.h>
using namespace std;
int a[5000005],b[5000005],t,m,n,k,cnt;
int main(){
    b[0]=1;//乘法为1 
    for(int i=1;i<=5000000;i++){//打表预处理 O(n)
        a[i]=a[i/10]/*上一位*/+(i%10);//和 
         b[i]=b[i/10]*(i%10);//积 
    }
    b[0]=-1;//标记(乘法无负数) 
    cin>>t;
    for(int i=1;i<=t;i++){
        cnt=0;
        cin>>m>>n>>k;
        for(int j=m;j<=n;j++){
            if(a[j]==k&&b[j]>b[cnt]){//判断找最大 
                cnt=j;
            }
        }
        cout<<cnt<<" "<<b[cnt]<<endl; 
    }
    return 0;
}


三.智能公交

1.题目大意

马路上总共有 n 个公交站台,编号从1,2,n,有一辆智能公交车会在这 n 个站台之间穿梭。

如果智能公交上没有乘客,那么智能公交就会停靠在 x 站台。

有人要乘坐智能公交,只需要按动公交站台上的按钮,智能公交就会快到到达相应的站台。

例如,有人想从第五个公交站台到第十个公交站台,那么公交车会先从第 x 个站台跑到第五个站台,然后再从第五个站台跑到第十个站台,然后再回到第 x 站台停下。(智能公交可以随意的双向移动,不需要考虑智能公交是否掉头转弯等因素)假设相邻的两个站台的距离都是 1 千米,那么智能公交总共行走了 |x-5|+|5-10|+|10-x| 的距离。

现在有 m 个人要依次乘坐智能公交,每个人都会等待智能公交停在 x 站台之后在按动当前站台按钮准备乘坐公交。现在已知第 i 个人都是从 a 站台到 b 站台。请你计算 x ,使得智能公交移动距离最短。最终输出 x 和最短的距离,x 若有多个,输出最小的一个。


2.比赛中的思考
        半暴力半规律,运行错误long long 占空间

3.解题思路
        等差数列、差分解答,往左跑在a~b区间额外的距离和往右跑在a~b区间额外的距离,原数组求前缀和是求总时间,相当于sum,等差数列是差分数组,此处差分转原数组,表示各个点到a和b额外的距离

4.AC代码

#include<bits/stdc++.h>
using namespace std;
const int N=5e5;
long long pr[N],s[N],pr1[N],sum=0,n,m,s1[N],cnt=1e18,p,a,b;
int main(){
    
    cin>>n>>m;
    
    for(int i=1;i<=m;i++){
        cin>>a>>b;
        pr[a-1]+=2;
        s[b+1]+=2;
        
        sum+=(b-a)*2;
    }
    
        
    for(int i=n;i>=1;i--){
        pr[i]+=pr[i+1];
            
        pr1[i]=pr1[i+1]+pr[i];
            
    }
    for(int i=1;i<=n;i++){
        s[i]+=s[i-1];
    
        s1[i]+=s1[i-1]+s[i];
    
    
        if(s1[i]+pr1[i]<cnt){
            p=i;
            
            cnt=s1[i]+pr1[i];
            
            
        }
        
    }
    cout<<p<<" "<<cnt+sum<<endl;
}

四.异或和
        1.题目大意

多个集合中总共有 n 个数字,并且已知每个数字的大小 a​i​​ 和属于某个集合 b​i​​。

在一个集合中选择一个数字,收益为这个数字的大小,选择多个数字,收益为这些数字的异或和。

总收益为每个集合的收益之和。注意:最多从中选择 m(m≤n) 个数字,使这些数字总收益最大。
        2.比赛中的思考
没做
        3.题解思路
            此时不难想到对于每一个集合进行一次如上操作,但若这样开成三维数组会超内存,故可以之用二维数组,但每次使用后清空。这样就需要再开一个数组 表示第i组选j个数最大的异或值。每一组结束 dp 预处理操作后存入 num 中,最后使用一个分组背包即可得到最大异或值。但是要注意,在每一组 dp 预处理时, 应循环到组中元素的个数而不是n否则会超时。 由于n的总值仍是2000,所以操作时组和i的循环次数总和仍为2000,时间复杂度不变。
        4.AC代码
          

 #include<bits/stdc++.h>
using namespace std;
int n,m,dp[2005][2050],num[2005][2005],dpp[2050],zz[2005];
// dp[i][j]表示看到前i个数,得到收益j至少所需要的数字个数,num[i][j]表示第i组j个数最大的收益值
vector<int>ve[2005];
int main() {
    cin>>n>>m;//n个数字,最多选m个数字 
    for(int i=1;i<=n;i++){//输入n个数字 
        int x,y;//x是数字大小,y是所属集合编号 
        cin>>x>>y;
        ve[y].push_back(x);//y集合的vector进入一个x 
        zz[y]++;//集合y的元素个数加一个 
    }
    for(int i=1;i<=n;i++){
        for (int j=1;j<=2047;j++){
            dp[i][j]=1e9;
        }
    }
    for(int zu=1;zu<=2000;zu++){//遍历所有的组 ,一组一组的处理 
    
        if(zz[zu]!=0)//如果这一组的元素不为空     
            dp[1][ve[zu][0]]=1; //第一个数 得到这一组的第一个数的收益值通过选择这一个数做到
              
        for(int i=2;i<=zz[zu];i++){//遍历这一组剩下的数字 
        // 前i个数,得到这个组的第i个数的收益可以通过直接选择这个数本身做到 
            dp[i][ve[zu][i - 1]]=1; 
            for(int j=1;j<=2047;j++){//遍历所有可能的数字(收益值) 
                if(dp[i-1][j]!=1e9){//如果前i-1个数字产生这个收益所用的最小数字个数存在(不为初始值) 
                
                //前i个数产生j这个数字(收益)的数字应用个数是前i个数和前i-1个数的使用数字个数的最小值 
                    dp[i][j]=min(dp[i][j],dp[i-1][j]);
                
                //前i个数字产生j^这一组的第i个数字产生的新数字(收益)的数字使用个数是产生j的数字个数+1,和本来就有的数字个数的最小值 
                    dp[i][j^ve[zu][i-1]]=min(dp[i-1][j]+1,dp[i][j^ve[zu][i-1]]);
                }
            }
        }
        
        for(int j=1;j<=2047;j++){//遍历所有的收益 
            if(dp[zz[zu]][j]!=1e9)//如果这一组的数字个数对应产生j这个数字(收益),所使用的最小数字个数存在,即能异或出这个数字 
            //num数组记录这一组对应 这些数字个数 能得到的最大数字 
                num[zu][dp[zz[zu]][j]]=j;
                
        }
        for(int i=1;i<=zz[zu];i++){//dp数组重新初始化一下 
            for (int j=1; j<=2047;j++){
                dp[i][j]=1e9;
            }
        }
    }
    for (int i=1;i<=2000;i++) {//遍历所有组 
        for (int j=m;j>=1;j--) {//能选的数字个数最多是m个 
            for (int k=1;k<=zz[i];k++) { // 最后的枚举,只枚举到本组的个数
                if (j>=k)//能选这个组的k个数字
                //j个数字产生的数字收益最大值要么不变,要么就是往前推k个数,从第i组选k个数字的最大收益累加 
                    dpp[j]=max(dpp[j],dpp[j-k]+num[i][k]);
            }
        }
    }
    cout<<dpp[m];//输出m个数字的最大收益 
    return 0;
}


            
        总结:
            下次认真做,多调试,不出低级错误

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
CSP-J复赛pdf是指CSP-J的复赛的PDF文件。CSP-J是中国计算机学会(CCF)主办的一项高中生计算机科学与编程的竞复赛CSP-J竞的第二个阶段,参者需要通过初选拔才能进入复赛CSP-J复赛pdf中包含了以往CSP-J复赛的试和答案。这些试主要涵盖了计算机科学与编程的各个方面,包括编程语言、算法、数据结构、计算机网络等。参者可以通过研究历,了解CSP-J竞型和出风格,提高自己的解能力和编程水平。 CSP-J复赛pdf对于参者来说具有很大的参考价值。通过研究这些试,参者可以了解到高水平的计算机科学与编程问是如何被设计和解决的。同时,参者还可以通过尝试解答这些试,检验自己的知识和技能,发现自己的不足之处并进行改进。 此外,CSP-J复赛pdf还对教师和学生进行教学和学习有很大的帮助。教师可以利用这些试作为教学资源,丰富教学内容和方法,提高学生的计算机科学与编程能力。学生可以通过研究这些试,扩展知识面,提升解决问和编程的能力。 总之,CSP-J复赛pdf是一份重要的学习和竞资料,对参者、教师和学生都具有很大的参考和帮助作用。通过研究这些试,大家可以更好地了解计算机科学与编程的相关知识和技能,并提升自己的竞和学习能力。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值