CSP-J复赛模拟赛1 王晨旭补题 2026.10.2

一,前言

去年也是成功的又没有拿到一奖啊,于是有了这篇文章。。。

今年的集训从2号开始,也是终于过了一个完整的生日啊(老粉dddd)

废话不多说,看看今年进步了多少吧

二,成绩

1,双面展签【100/100】第一题你被毕业了

2,异组参照【30/100】就做了5分钟,得多少分你别管

3,翻翻转转【0/100】申诉后【100/100】我从地狱回来了(整个班里最熟的是这个题)

4,对照实验【55/100】你配第四题?

总分:【285/400】看不到rank这次高分班似乎不公布排名

依旧不看难度瞎排,除了第一题别的全部打乱

题一直接毕业好吧(累了搞不动了)

三,题二

第三题实在老仇人,所以本题没有赛上(最不尊重第二题的一把),5分钟敲了个暴力还漏了-1特判喜提30分

但其实这题出的挺好的,看看题面

实验室收到了 n 份读数。第 i 份读数的数值为 a​i​​,所属小组的编号为 c​i​​。

为了交叉核对,每份读数都需要寻找一份来自不同小组的读数作为参照。对于第 i 份读数,可以选择任意满足 cj≠ci​​ 的读数 j,希望两者数值差的绝对值 ∣ai−aj∣尽可能小。

请对每份读数分别求出这个最小差值。如果不存在来自不同小组的读数,则该份读数的答案为 −1。

不同小组可以出现相同数值,此时差值为 0。每份读数的参照选择互不影响,同一份读数可以被多个对象选作参照。只需输出最小差值,不需要输出参照对象的编号。

是这样的一个思路,可以用结构体或者vector进行存储

存完后打排序,规则:按数值从小到大排,组号随便

排完之后,相同小组编号的不一定都在一块,但我的答案一定是出于不同组的,这样搞一个前驱数组,记录左边离自己最近的不同组的元素编号(因为已经数值排序了,所以距离最小就是差值最小),再搞一个后继数组,记录右边离自己最近的不同组的元素编号,左边差值最小和右边差值最小取一个最小值就是答案

cpp 复制代码
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
const int N=2e5+5;
int n,a[N],c[N],lnx[N],rnx[N],ans[N];
int main(){
    cin>>n;
    vector<array<int,3> >v;
    for(int i=1;i<=n;i++){
        cin>>a[i]>>c[i];
        v.push_back({a[i],c[i],i});
        ans[i]=INF;
    }
    sort(v.begin(),v.end());
    lnx[0]=-1;
    for(int i=1;i<n;i++){
        if(v[i][1]==v[i-1][1]){
            lnx[i]=lnx[i-1];
        }
        else{
            lnx[i]=i-1;
        }
        if(lnx[i]!=-1){
            ans[v[i][2]]=min(ans[v[i][2]],v[i][0]-v[lnx[i]][0]);
        }
    }
    rnx[n]=-1;
    for(int i=n-2;i>=0;i--){
    	if(v[i][1]==v[i+1][1]){
    		rnx[i]=rnx[i+1];
		}
		else{
			rnx[i]=i+1;
		}
		if(rnx[i]!=-1){
			ans[v[i][2]]=min(ans[v[i][2]],v[rnx[i]][0]-v[i][0]);
		}
	}
	for(int i=1;i<=n;i++){
		if(ans[i]==INF)	cout<<ans[i]<<"\n";
		else cout<<ans[i]<<"\n";
	}
}

vector套array连cmp都不用排直接就能按照第一个元素从小到大排序(相同则看第二个从小到大排序)

四,题三

往年今日 2024.10.1T2

https://blog.csdn.net/2301_80046199/article/details/142671263?spm=1001.2014.3001.5501
(西部牛仔开场音乐)许多年未见,原来你已经成为第三题了吗。。。

可惜了,今天的我也变强了,接受审判吧!!!(bushi)

在场上看到这个题也是直接没有绷住啊,仇人见面分外眼红,最后还阴了我一手题目名tm的竟然不是flip而是filp这个滚木东西,我滴妈也是直接亏掉100分啊

回归正题,24年做这个的时候还毫无思路的找规律打表呢,讲完是二分也不会写,今年会打二分了只觉得这个题超级简单

换了一个老师换了一种做法,但是我还是要用24年的二分

题面回顾一下(我去竟然24年忘记放题面了)

定义字符串序列:

s0=1

si=si−1+si−1‾(i≥1)

其中 si−1‾​表示把 si−1​​ 的每一位取反,即把 0 变成 1,把 1 变成 0。例如:

s0=1

s1=10

s2=1001

s3=10010110

现在给出若干询问,每次询问一个位置 x,求 s114514 的第 x 个字符。

这个题不是二分答案,而是二分查找

初始是1,翻转左边相同依然是1,右边取反变为0,按照左不变右取反的思路

在最靠近x的++2的n次方的连续序列++ 或者直接++2的30次方的连续序列(0,1,2,3,4...2^30)++中查找x,设置一个标记字符s,x在mid右边s就取反,x在mid左边s就不变,直到mid与x完全相同 当前标记字符s就是答案

其实我不知道为什么这么做,只是通过小样例反推验证的正确性,让3作为样例

2^3位 10010110 左4和右4取反,初始是1,3在左4,1不变

2^2为 1001 左2和右2取反,3在右2 1变为0

2^1为 01 左1和右1取反,3在左1 0不变

答案即为0

在初始大区间中判断,处于左半区间字符不变,右半区间字符取反,直到走到单字符区间

再说推论,纯两层的情况下,左边的左边和右边的右边(取反两次等于取反0次)的结果相同,左边的右边和右边的左边结果相同,也有初始是0在左边就取反在右边就不变的这么一种做法,观察序列每4位均为0110或1001即说的两层情况后面再往更高维扩展就是正解

(好诡异的一个题,只能说当年没思路不怪自己啊)

AC代码

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
void f(int l,int r,int s,int t){
	double mid=(l+r)*1.0/2; 
	if(mid==t){
		cout<<s<<"\n";
		return ;
	}
	if(t>mid){
		if(s==0)	s=1;
		else if(s==1)	s=0;
		f(ceil(mid),r,s,t);
	}
	else{
		f(l,floor(mid),s,t);
	}
}
int main(){
	freopen("flip.in","r",stdin);//空悲切
	freopen("flip.out","w",stdout);
	int T,n;
	cin>>T;
	while(T--){
		cin>>n;
		if(n==1){
			cout<<1<<"\n";
		}
		else{
			f(1,pow(2,ceil(log2(n))),1,n);
		}
	}
}

五,题四

直接题面,也是第一个考场上拿50分以上的背包题

非常简单,考场代码改6个字符能A的,但可惜背包刷的少调不出来

训练营准备了 n 项实验,编号为 1∼n。完成实验 i 需要 t​i​​ 分钟,可以获得 v​i​​ 分。

学生可以任选一些实验,按照编号从小到大的顺序完成。每项实验最多做一次,所有被选实验的耗时之和不能超过 B 分钟。

为了鼓励对照分析,如果编号相邻的实验 i 和 i+1 都完成,还能额外获得 b​i​​ 分奖励,不增加耗时。

每一对满足条件的相邻实验都分别产生奖励。例如,连续完成实验 2,3,4,可以同时获得 b​2​​ 和 b​3​​;如果只完成实验 2,4,则不会因这两个实验获得相邻奖励。

请计算最高能够获得多少总分。允许不做任何实验,此时耗时和得分均为 0,也不要求把时间恰好用完。

正解思路,设立三維dp数组

dpij0:当前第i个物品容量为j,当前物品不拿的最大收益

dpij1:当前第i个物品容量为j,当前物品必拿的最大收益

状态转移也是非常好写了

不选择第i项,那跟i-1项没有关系,直接继承

dpij0=max(dpi-1j0,dpi-1j1);
选择第i项,那就分为了两种情况

i-1项被选择,则dpij1=dpij-c\[i]0+vi

i-1项没有被选择,则dpij1=dpij-c\[i]1+vi+bi

最后一项可以不选,容量消耗也不一定是B

所以答案是max(dpnj0,dpnj1);

j:0到B循环

正片开始,考场思路

普通两维dp

dpij:当前第i个物品容量为j最大收益

但是状态转移就有点说法了

什么都不做好说,直接继承上一个物品

cpp 复制代码
dp[i][j]=max(dp[i][j],dp[i-1][j]);

只选当前这一个,没有组合额外奖励(注意判断剩余容量)

cpp 复制代码
dp[i][j]=max(dp[i][j],dp[i-1][j-c[i]]+v[i]);

有组合额外奖励呢?

连续两个?

cpp 复制代码
dp[i][j]=max(dp[i][j],dp[i-2][j-c[i]-c[i-1]]+v[i]+v[i-1]+b[i]);

连续三个?

cpp 复制代码
dp[i][j]=
max(dp[i][j],dp[i-3][j-c[i]-c[i-1]-c[i-2]]+v[i]+v[i-1]+v[i-2]+b[i]+b[i-1]);

也是直接得到规律了好吧,直接for循环遍历连续的个数,注意容量越界判断

中间如果组合断开怎么办?没关系,我们连续转移时候继承的一定是前面的最优方案,而这最优方案具体组成,结构是什么,是否断开我一律不管,只要最优就满足dp

扣45分是因为状态转移没写好而小样例体现不出来,max的首项我没有写dpij,而是写了dpi-1j或者dpk-1j等等诡异的东西,本质上没有理解这是一个选择问题,来取三方案最优的话首项肯定要是自己啊啊啊(哭)

AC代码

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int c[305],w[305],b[305],f[305][3005];
int main(){
	freopen("lab.in","r",stdin);
	freopen("lab.out","w",stdout);
	int n,B;
	cin>>n>>B;
	for(int i=1;i<=n;i++){
		cin>>w[i]>>c[i]; 
	}
	for(int i=1;i<n;i++){
		cin>>b[i];
	}
	for(int i=1;i<=n;i++){
		for(int j=B;j>=0;j--){
			f[i][j]=max(f[i][j],f[i-1][j]);
			if(j>=w[i]){
				f[i][j]=max(f[i][j],f[i-1][j-w[i]]+c[i]);
			}
			int tot=w[i],ctot=c[i],btot=0;
			for(int k=i-1;k>=1;k--){
				tot+=w[k];
				ctot+=c[k];
				btot+=b[k];
				if(j>=tot){
					f[i][j]=max(f[i][j],f[k-1][j-tot]+ctot+btot);
				}
			}
		}
	}
	/*for(int i=1;i<=n;i++){
		for(int j=0;j<=B;j++){
			cout<<f[i][j]<<" ";
		}
		cout<<"\n";
	}*/
	cout<<f[n][B];
}

可读性可能比较差,见谅

六,总结

生日过完了呜呜呜呜,今年加把劲一定要过!!!!!!事不过三

相关推荐
周杰伦fans2 小时前
轻量模型高并发推理优化技巧
开发语言·人工智能·c#
Frank_refuel2 小时前
【C++项目】手撕JRpc框架(单机怎么优化到分布式思想)
开发语言·c++
卓怡学长2 小时前
w192基于springboot“考研情报站”微信小程序设计与实现
java·spring boot·spring·微信小程序·intellij-idea
wuminyu2 小时前
JEP491中synchronized关键字引发的平台线程钉住解决方法简介
java·linux·c语言·jvm·c++
可乐鸡翅yeah_2 小时前
fmp4 碎片 MP4 格式 HLS 流,和传统 TS 分片有什么不一样
开发语言·javascript·ios·音视频·safari·m3u8·m3u8在线播放
流形填表2 小时前
多校区题库标准化:Excel模板与校验脚本
开发语言·python
步行cgn2 小时前
AOP 通知的类型详解
开发语言
weixin_307779132 小时前
从“人操作仪器”到“Agent 自主发现”:OPL 金属材料自驱动实验室 MVP 技术论述
开发语言·人工智能·算法·架构
笑鸿的学习笔记3 小时前
C++笔记之SIMD与SSE指令集
开发语言·c++·笔记