MENU
庄闲游戏 0000-0000-0000
庄闲游戏
庄闲游戏
庄闲游戏
庄闲游戏
你的位置:庄闲和游戏官方网站 > 联系我们 > 庄闲和app 【未来号原创】2024 CSP-J第二轮真题解析、满分代码来啦!

庄闲和app 【未来号原创】2024 CSP-J第二轮真题解析、满分代码来啦!

时间:2026-01-14 02:56 点击:90 次

庄闲和app 【未来号原创】2024 CSP-J第二轮真题解析、满分代码来啦!

昨天刚结束的CSP复赛,未来号教研团队就火速对题目做了解读,以下是CSP-J每道题的具体分析

第一题:

{jz:field.toptypename/}

图片

难度:普及-

知识点解析:模拟,可以简化为去重计数,有很多种方法。比较简单的是用字符串数组+sort+unique函数,或者字符串数组+sort+枚举。也可以像下面的代码用map来去重。

#include <bits/stdc++.h>using namespace std;int n;string s;map<string,int> mp;int main(){ cin>>n; for(int i=1;i<=n;i++) { cin>>s; mp[s]=1; } cout<<52-mp.size(); return 0;}

第二题:

图片

难度:普及-

知识点解析:模拟,用二维数组保存地图,记录是否到过某个点,模拟操作过程即可。题目还很“贴心”的把右转如何用代码表示都提示了。多组数据,记得重置数组。

#include <bits/stdc++.h>using namespace std;int T,n,m,k,x,y,d,ans;char a[1010][1010];int vis[1010][1010];int dx[4]={0,1,0,-1};int dy[4]={1,0,-1,0};int main(){    cin>>T;    while(T--)    {        cin>>n>>m>>k;        cin>>x>>y>>d;        for(int i=1;i<=n;i++)            for(int j=1;j<=m;j++)                cin>>a[i][j];                memset(vis,0,sizeof(vis));        vis[x][y]=1;        ans=1;        for(int i=1;i<=k;i++)        {            int nx=x+dx[d];            int ny=y+dy[d];            if(nx>=1&&nx<=n&&ny>=1&&ny<=m&&a[nx][ny]=='.')            {                x=nx;                y=ny;                if(vis[x][y]==0)                {                    vis[x][y]=1;                    ans++;                }            }            else                d=(d+1)%4;        }        cout<<ans<<endl;    }    return 0;}

第三题:

图片

{jz:field.toptypename/}

难度:普及/提高-

知识点解析:思维,贪心。

首先,同样根数的数字,6根(0,6,9),5根(2,3,5),2根(1),4根(4),3根(7),7根(8)。

先从n比较小开始考虑,尽量用最少的数字位数:

n=1,没有方案;n=2,1;n=3,7;n=4,4;n=5,2;n=6,6;n=7,8,到这里我们发现,能用一个数字解决就不用两个数字。

n=8,10;n=9,18;n=10,22;n=11,20;n=12,28;n=13,68;n=14,88;两个数字可以解决。

n=15,108;n=16,188;n=17,200;n=18,208,n=19,288;n=20,688;n=21,888;三个数字可以解决,注意这里的数字是上升的。

n=22,1088,从这里开始,我们可以说发现了规律,末尾用8是最优选择,因为不用8剩下的根数多,而剩下的根数多,意味着数字大(由上一排得出的规律)。

这一题可能比较容易想到动态规划,从思维方法上也很直观,庄闲和游戏网但是因为对应的整数很大,会卡在大整数如何处理上。

打表部分手算可能出错,可以暴力枚举验证一下。

#include <bits/stdc++.h>using namespace std;int T,n;int a[22]={0,-1,1,7,4,2,6,8,10,18,22,20,28,68,88,108,188,200,208,288,688,888};int main(){ cin>>T; while(T--) { cin>>n; if(n<=21) cout<<a[n]<<endl; else { int l=n%7; if(l==0) l=7; cout<<a[14+l]; for(int i=1;i<=(n-15)/7;i++) cout<<8; cout<<endl; } } return 0;}

第四题:

图片

难度:提高

知识点解析:动态规划。

部分分一,r=1,依次判断每个人,序列中从1开始的第2到K个中是否包含C。

建站客服QQ:88888888

部分分二,n<=10,r<=5,DFS,枚举每种可能性。

我们暂时不考虑复杂度,先把思路理清:dp[i][j][k]表示第i轮到第j个人的数字k是否可行。

如果可行,则可以走到 dp[i+1][j1][k1],其中j1!=j,k1是在j1中从k可以到达的数字。

我们可以先预计算每个人的序列关系,建立a->b是否可走的关系图。

这个朴素的动规,用图论的思维来思考每个数字的跳转关系,但是状态的复杂度是r*n*S,转移的复杂度是n*S。复杂度过高。

思考一下该如何优化状态和转移?

dp[i][j]表示第i轮的结尾是第j个数字是否可行

这里把n个人的序列拼接起来,j是拼接后的序列的位置。因为n个序列的长度和不超过2*10^5,状态的复杂度降低到了可接受的程度。

假设拼接后的序列是a,如果第j个数字属于第x个人,第x个人的第一个数排在第y个,那么dp[i][j]取决于[max(j-K+1,y),j-1]这个区间的某个数能否与第i-1轮的其他人的可以达到的数字接上

用f[j]表示a[j]能否与上一轮的其他人的可以达到的数字接上。我们可以用前缀和在O(1)的复杂度计算这个区间内是否存在可接。

接下来思考f[j]如何得到,分情况讨论a[j]:

①如果a[j]只出现了一次,显然不能;

②如果a[j]出现了多次,但都是同一个人的序列内,也不能;

③如果a[j]出现了多次,且在上一轮可行的位置不是第x个人的区间,可以。

也就是说,我们每一轮,要维护每一个数字可行的位置。这里又有一个小技巧优化,其实我们不需要知道所有的位置,如果只有一个人可行,则记录该人,如果两个人都可行,则所有的人都可行。

到这里为止,我们把转移优化到了O(1),整体复杂度是T*n*L, 5*100*2*10^5 = 10^8,维护多个数组的常数比较大,所以题目给了2s的时限。

#include <bits/stdc++.h>using namespace std;int T,n,k,q,l,r,c;int a[200010]; //拼接后的数组int dp[110][200010]; //第i轮,以第j个数字结尾是否可行int f[110][200010];  //第i轮第j个数a[j]是否能与上一轮的其他人的相同数字的可行方案接上int sum[200010]; //f数组的前缀和int p[110][200010]; //第i轮第j个数a[j]在上一轮的情况,0表示不可行,n以内的数x表示只有被编号x的人可行,1e6表示至少两个人都可行int b[200010]; //i属于哪个人int st[100010]; //第i个人的起点int ans[110][200010]; //第i轮到数字j是否可行int main(){    cin>>T;    while(T--)    {        cin>>n>>k>>q;        int len=0;        for(int i=1;i<=n;i++)        {            cin>>l;            st[i]=len+1;            for(int j=1;j<=l;j++)  //1到len            {                cin>>a[++len];                b[len]=i;            }        }        memset(dp,0,sizeof(dp));        memset(p,0,sizeof(p));        memset(f,0,sizeof(f));        memset(ans,0,sizeof(ans));        p[0][1]=1e6; //第0轮,1不管几个都可行        for(int i=1;i<=100;i++)        {            for(int j=1;j<=len;j++)            {                if(p[i-1][a[j]] && p[i-1][a[j]]!=b[j])  //上一轮a[j]的情况不是0,且不是自己                    f[i][j]=1;                sum[j]=sum[j-1]+f[i][j];                dp[i][j]=(sum[j-1]-sum[max(st[b[j]],j-k+1)-1]>0)? 1:0;                if(dp[i][j]>0)                {                    ans[i][a[j]]=1;                    if(p[i][a[j]]==0)                        p[i][a[j]]=b[j];                    else if(p[i][a[j]]!=b[j])                        p[i][a[j]]=1e6;                }            }        }        for(int i=1;i<=q;i++)        {            cin>>r>>c;            cout<<ans[r][c]<<endl;        }    }    return 0;}

图片

综上,J组的前面2道题目难度还行,第3题难度就上升了。而且,与去年相比,今年的题目都变得比较长,这也在一定程度上考察了同学们的耐心😂

预祝所有信奥选手都能取得自己理想的成绩!

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报。
服务热线
官方网站:oky-messe.com
工作时间:周一至周六(09:00-18:00)
联系我们
QQ:2852320325
邮箱:oky-messe.com @qq.com
地址:武汉东湖新技术开发区光谷大道国际企业中心
关注公众号

Copyright © 1998-2026 庄闲和游戏官方网站™版权所有

oky-messe.com 备案号 备案号: 沪ICP备07013924号-1

技术支持:®  RSS地图 HTML地图

回到顶部