当前位置:   article > 正文

CSP-J2023 赛后总结_csp-j2023 公路

csp-j2023 公路

目录

· 比赛概况

· 比赛过程

· 题目分析

T1【小苹果(apple) 】

· 题目大意

· 赛中思考

· 解题思路

· AC代码 & 赛时代码

· 附

T2【公路(road)】

· 题目大意

· 赛中思考

· 解题思路

·  AC代码

· 赛后总结


· 比赛概况

洛谷自测:

总分: 165 / 400

T1T2T3T4
AC55010

T2很可惜,因为累加器没开long long ,WA了45分 (QwQ)

· 比赛过程

按照顺序主攻前两题

第一题 , 不出意外不是一道纯暴力能秒的题,是一道需要推规律的约瑟夫环

第二题 ,读题以为挺难,其实不然,预处理 +  循环累加 就能过 ,可惜忘记开 long long 了。。。

第三题 ,我太弱 , 读不懂题目一堆奇妙的不明数学语言 , 暴力也没法打 , 只能不可以总司令 , 循环输出 NO ,一分没骗到

第四题 , 万恶的图论 , 赛前练的全忘了 ,输出-1,拿了10分

· 题目分析

ps : T3 T4 直到赛后也没看懂。。。所以这里我只说前两题

(我会继续研究 T3 T4 , 看懂后尽快更新这篇博客)

T1【小苹果(apple) 】

· 题目大意

共有 n 个苹果线性排列 , 每天从左侧第 1 个苹果起 ,每间隔 2 个苹果拿走一个苹果 , 每次拿完后,将剩下的苹果按原来的顺寻重新排成一列。

问:

1. 多少天能取走所有的苹果

2. 第 n 个苹果是在第几天被拿走的

· 赛中思考

先打了个暴力 , 看眼样例发现 n \leq  10^18  ,暴力肯定过不了全部样例 ,但是暴力代码也不要删掉 ,因为可以在写出正确代码后用来写对拍验证 (对拍我们待会说),最终也是推出了规律 ,详见题解部分。

· 解题思路

首先要明确本题是一道典型的约瑟夫环问题 ,暴力过不了 , 考虑优化思路

我们用数字代表这个苹果的编号 , 每次标红的数字为被拿走的苹果的编号 ,当 n = 8 时 ,

第 0 天(初始状态): 1        2        3        4        5        6        7        8

第 1 天 :1        2        3        4        5        6        7        8

          =    2        3        5        6        8

第 2 天 :2        3        5        6        8

          =   3        5        8

第 3 天 :3        5        8

          =   5        8

第 4 天 :5        8

          =   8

第 5 天 :8

          =   无

则当 n=8 时,5 天能拿完所有苹果 , 编号为 n (8) 的苹果第 5 天被拿走。

首先看第一问: 多少天能取走所有苹果

这个问题等价与 求第几天时苹果的数量等于0 , 那我们就可以求出每次会减少多少苹果 ,再用当前苹果数减去即可 。

根据刚才举的样例 , 可以发现每次取苹果时 ,只要苹果的数量不为 0 ,总是要取走第 1 个苹果 

然后 2 ~ n 这个范围里,因为每隔两个苹果都要取走一个 ,也就是每 3 个苹果都要拿走一个 ,

所以这个部分共要取走 (n-1)/3 个苹果 。

综上所述 ,记当前剩余的苹果数量为 cnt  ,则当前需取走的苹果数量为 1+((cnt-1)/3)

只需要循环对 cnt 做减法并判断天数即可

现在我们再来看第二问 :第 n 个苹果是在第几天被拿走的

首先,如果第 n 个苹果没有被取走 ,那么它一定排在这个序列的最后 ,所以想要判断第 n 个苹果当前是否被取走 ,就判断一下当前这个序列的最后一个位置是否会被取走 ,根据上面第一问说的 ,每次会拿走第一个 ,后面 2 ~ cnt 这个序列每有三个苹果就会取走一个 ,所以就判断一下cnt-1(也就是当前苹果数量-1 ,也可理解为 2 ~ cnt 这个序列的苹果数量)是否可以被 3 整除

如果可以 ,就把当前的天数记录下来并标记为已取走 ,避免重复计算 ,最后输出即可

奉上代码

· AC代码 & 赛时代码

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. long long n;
  4. int main(){
  5. freopen("apple.in","r",stdin);
  6. freopen("apple.out","w",stdout);
  7. cin>>n;
  8. long long cnt=n,ans=0,ans1=0;
  9. bool flag=0;
  10. if(n==1){
  11. cout<<"1 1";
  12. return 0;
  13. }
  14. while(cnt>0){
  15. ans++;
  16. if((cnt-1)%3==0&&flag==0){
  17. ans1=ans;
  18. flag=1;
  19. }
  20. cnt-=(1+((cnt-1)/3));
  21. }
  22. cout<<ans<<' '<<ans1;
  23. fclose(stdin);
  24. fclose(stdout);
  25. return 0;
  26. }

· 附

这里给大家介绍一种竞赛中非常有用的一种技巧——对拍

首先:什么是对拍? 它的用途是什么呢?

对拍是一种在赛场或平时验证某一程序的正确性的程序 ,它可以检测你写出的 “正解程序”是否是正确的

对拍有两种实现方式 ,一种是写程序 ,一种是朴素的手动对拍(手动判断数据是否一致)

其中手动对拍非常的简单 ,只需要你写一个保证可以通过部分数据的暴力程序 ,和一个优化后但不确定正误的程序  ,分别执行 ,并使用相同的输入数据 ,对比两个程序的输出是否一致即可。

接下来我们来说一下对拍的程序实现:

想要对拍需要三个程序:

1.一个保证对的朴素算法 命名为 bl.cpp ,编译运行生成 bl.exe

2.正解代码 ,或优化后的代码 命名为 true.cpp ,编译运行生成 true.exe

3.随机数生成函数 rand.cpp , 编译运行生成 rand.exe

4.创建一个名为 data.in  的文件 , 用来输入随机数据

5.创建 bl.out 和 true.out 分别用来接收 1 和 2 两个程序的输出

6.创建一个对拍 C++ 项目 ,在里面调用以上的文件并比较,最后反馈出结果

对拍程序代码:

  1. #include<bits/stdc++.h>
  2. int main(){
  3. for(int i=1;i<=S;i++){
  4. system("rand.exe");
  5. double start=clock();
  6. system("true.exe");
  7. double end=clock();//计算所用的时间,可以时间clock()函数,分别记录时间Start,End,相减即可得出答案
  8. system("bl.exe");
  9. if(system("fc true.out bl.out")){
  10. puts("Wrong Answer!");
  11. return 0;
  12. }
  13. else{
  14. printf("Accepted! 测试点 %d 用时:%lf",i,end-start);
  15. }
  16. }
  17. return 0;
  18. }

随机数rand.cpp的代码 ,数据范围要根据实际题目的数据范围定 ,下面的代码可以重复生成<mod 的随机数

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. #define ll long long
  4. ll Random(ll mod)
  5. {
  6. ll ans = 2147483647;
  7. return ans = ans * rand() % mod + 1;
  8. }
  9. int main(){
  10. ll n;
  11. ll mod=();//根据题目定范围
  12. while (1)
  13. {
  14. n = Random(mod);
  15. printf("%lld\n", n);
  16. }
  17. return 0;
  18. }

正解代码的模板,注意前面 freopen 的写法

  1. #include<bits/stdc++.h>
  2. int main(){
  3. freopen("data.in","r",stdin);
  4. freopen("true.out","w",stdout);
  5. //程序
  6. fclose(stdin);
  7. fclose(stdout);
  8. return 0;
  9. }

暴力的模板,同样注意 freopen 

  1. #include<bits/stdc++.h>
  2. int main(){
  3. freopen("data.in","r",stdin);
  4. freopen("bl.out","w",stdout);
  5. //程序
  6. fclose(stdin);
  7. fclose(stdout);
  8. return 0;
  9. }

T2【公路(road)】

· 题目大意

小苞准备开着车沿着公路自驾。

公路上一共有 n 个站点,编号为从 1 到 n。其中站点 i 与站点 i+1 的距离为 v_{i} 公里。

公路上每个站点都可以加油,编号为 i 的站点一升油的价格为 a_{i}​ 元,且每个站点只出售整数升的油。

小苞想从站点 1 开车到站点 n,一开始小苞在站点 1 且车的油箱是空的。已知车的油箱足够大,可以装下任意多的油,且每升油可以让车前进 d公里。问小苞从站点 1 开到站点 n,至少要花多少钱加油?

· 赛中思考

一道贪心的题目(?) ,我的思路是预处理一下 ,算出每一段路的油是从哪个站点加的 ,然后累加求和 ,最后输出

· 解题思路

首先读入数据 ,注意,因为路程的数量为n-1 , 所以输入时要做好处理

然后我开了两个数组 ,minn[] 和 id[] ,

其中 minn 数组是用来前缀和(又像是动态规划) 求当前这个站点以及站点前的油钱最小值 ,

id 数组是用来存当前从  ( i-1 )~ i,这段距离消耗的油是从哪个站点加的。

然后开始循环判断 ,找到一段 id[i] 的值相等的序列 ,表示这一段连续的路程的油都是从 id[i] 的站点加的 ,然后把这个站点的路程累加 ,

因为只出售整数升的油 ,所以需要加一个上取整函数ceil() ,

然后————这里又有一个细节 ,因为加了上取整函数 ,所以可能会比计划要走的距离多走一部分 ,

比如说:站点 1 到 2 之间的距离为 10 公里 ,每一升油可以走 4 公里 ,这时 ceil( 10 / 4 ) 为 3 ,

3 * 4 = 12 也就是买的 3 升油实际上可以走 12 公里 ,所以在计算后面的距离的时候就要减去这 2 公里

这一部分就不需要多走一遍了 ,所以要把每一次计划走的距离减去这个多走的距离 ,避免重复计算 ,然后注意处理好下标的变化 ,最后输出即可

·  AC代码

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. int minn[100005];
  4. int id[100005];
  5. int n,d,v[100005],a[100005],x=0;
  6. long long ans=0;
  7. int main(){
  8. freopen("road.in","r",stdin);
  9. freopen("road.out","w",stdout);
  10. cin>>n>>d;
  11. for(int i=2;i<=n;i++) cin>>v[i];
  12. for(int i=1;i<=n;i++) cin>>a[i];
  13. minn[1]=a[1];
  14. id[1]=1;
  15. for(int i=2;i<n;i++){
  16. if(a[i]<minn[i-1]){
  17. id[i]=i;
  18. }
  19. if(a[i]>=minn[i-1]){
  20. id[i]=id[i-1];
  21. }
  22. minn[i]=min(minn[i-1],a[i]);
  23. }
  24. id[n]=id[n-1];
  25. for(int i=1;i<n;){
  26. int j;
  27. long long sum=0;
  28. for(j=i+1;j<=n&&id[j-1]==id[i];j++){
  29. sum+=v[j];
  30. }
  31. sum-=x;
  32. x=ceil(1.0*sum/d)*d - sum;
  33. ans+=ceil(1.0*sum/d)*minn[i];
  34. i=j-1;
  35. }
  36. cout<<ans;
  37. fclose(stdin);
  38. fclose(stdout);
  39. return 0;
  40. }

呜呜呜呜呜呜呜呜呜呜呜呜呜呜呜呜呜呜我的45分 QwQ

· 赛后总结

这次的比赛其实发挥的并不是很好 ,前两题思路完全没错 ,可是竟然犯了数据类型的问题 ,

这次比赛我是奔着省一去的 ,但这 45 分 ,可能让我与它擦肩而过 ,也许这次充满遗憾 ,

可仔细一想 ,收获确实满满

虽有一些遗憾 ,但这毕竟我初中生涯的第一年 ,之后的路还长 ,谁也说不准未来会怎样

昨夜测完分后 ,漫步于小区:品碎玉之清辉兮,感世事之难料;觉枯蝶之须臾兮,叹年华之易老。

可转念一想——玉虽碎兮,已放清辉,不觉碎之可憾;蝶虽谢兮,已历春秋,不感谢之可惜。过去了的不必叹息,曾经逝去的,都将成为下一步航行的羽翼,助逐梦者扬帆远航。

“凡所过往,皆为序章”,把苦水铸成利剑,破现实之魔障,就彼岸之辉煌。这不正是CSP2023对我的意义吗?

(这辈子不会再犯 long long 开成 int 的错了)

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/72620
推荐阅读
相关标签
  

闽ICP备14008679号