一,前言
去年也是成功的又没有拿到一奖啊,于是有了这篇文章。。。
今年的集训从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 份读数的数值为 ai,所属小组的编号为 ci。
为了交叉核对,每份读数都需要寻找一份来自不同小组的读数作为参照。对于第 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 需要 ti 分钟,可以获得 vi 分。
学生可以任选一些实验,按照编号从小到大的顺序完成。每项实验最多做一次,所有被选实验的耗时之和不能超过 B 分钟。
为了鼓励对照分析,如果编号相邻的实验 i 和 i+1 都完成,还能额外获得 bi 分奖励,不增加耗时。
每一对满足条件的相邻实验都分别产生奖励。例如,连续完成实验 2,3,4,可以同时获得 b2 和 b3;如果只完成实验 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最大收益
但是状态转移就有点说法了
什么都不做好说,直接继承上一个物品
cppdp[i][j]=max(dp[i][j],dp[i-1][j]);
只选当前这一个,没有组合额外奖励(注意判断剩余容量)
cppdp[i][j]=max(dp[i][j],dp[i-1][j-c[i]]+v[i]);
有组合额外奖励呢?
连续两个?
cppdp[i][j]=max(dp[i][j],dp[i-2][j-c[i]-c[i-1]]+v[i]+v[i-1]+b[i]);连续三个?
cppdp[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];
}
可读性可能比较差,见谅
六,总结
生日过完了呜呜呜呜,今年加把劲一定要过!!!!!!事不过三