cf怒刷18道800分题然后参加了Codeforces Round 916 Div. 3(乐),目标是争取能从中感受一下比赛氛围再就是跳出舒适圈,以赛促学督促一下自己的算法竞赛学习。那就模仿佬们在比赛之后写写自己的感受感悟和题解吧,就当做自己的比赛日记了emmmm,之后打算每两天打一次比赛然后之后的时间去补题和知识点的内容,希望自己能在考研闲暇忙碌中抽出一些时间去在算法竞赛中学到一些知识,也提高一下自己的代码能力。
作为新人的第一篇算法日记,先聊聊自己的经历吧,前两年总是觉得算法竞赛耗时长、难度高,自己又超懒所以根本不想动也打消了出现在脑中不到一段时间的算法竞赛梦,那如今到了大三下临近考研一个多月为什么突然迈入自己前两年放弃的大坑呢?也许只是觉得自己的大学好像少了些什么,感觉不打算法竞赛的cs大学生不是一个正经的cs大学生(其实也不是),可能就是自己心底感觉有一些遗憾吧,总觉得自己的cs专业好像少了些什么东西。不过也没有时间加入ACM实验室参加icpc什么的了,听闻蓝桥杯相对简单,oi赛制有时不要求高深的算法的掌握度、暴力也能获得一些分数,相对自己短期的算法竞赛针对性学习也许有所希望,有希望那就去尝试一下吧,总不能留遗憾。目标可能就是蓝桥杯得个奖吧(目标不高得个奖就行省三也行(乐,不过还是希望尽可能高一点)),然后也算是在充分的准备软微复试的手撕代码了,也算是对考研复试的巩固(点头)。今天正好确定了2024蓝桥杯的时间,2024年4月13日,还有不到120天,还有考研初试和期末考试的压力,争取保持三天一次vp或者比赛然后坚持每日刷题补题。借用匈牙利诗人裴多菲的一句话吧:绝望之为虚妄,正与希望相同。
12.20的 Codeforces Round 916 (Div. 3)做出来了AB两道,之后的时间都在看C和D,能理解题目但完全不知道应该从何入手,感觉还是缺乏经验和对应的解题工具性的方法。主要在这里记录整理一下自己学习到的新知识以及做题的方法及心路历程,学习算法竞赛思维和做题能力。
A
方法:统计字母出现次数,若次数等于其字母序号,则num+1,最后输出num
B
方法: 例:654321逆序k=0,123456顺序最大为5。我们先将k=0完全逆序,然后从左端大到小依次顺序放至1的后端。如:k=2, 432156。 k=4。213456。
C(补题)
方法1:使用维护前后缀。
正常我们找a[i]前的所有数组的最大值的求法:先循环i=1到n,再二重循环1到i,时间复杂度为o(n^2),超过了本题所限制的时间复杂度。所以我们使用维护后缀方法。
维护前后缀:la[N],ra[N]。la[i]提前保存下来a[i]及以前(前缀)的最大值,只需要一次循环求最大值,提前循环一遍并用la[i]数组保存好该前缀的最大值。
我们用另一种思维想题:我们确定一个中间的数组,然后左边会有一个数组,我们找它左端的最大值,右边同理。即值=左端最大值+中间数组+右端最大值。例:lb[i-1]+a[i]+rc[i+1],仅需进行一次循环,不过我们不确定abc的顺序,所以分类讨论六种顺序。这就把所有的可能的和都遍历一遍了,遍历的过程对比保存最大值,即得最大值。
注:维护前后缀的作用是当求前缀或后缀的某种性质,例如本题是前缀最大值和后缀最大值,可以尽可能减少时间复杂度,将o(n^2)降至o(n)的复杂度。再就是找出规律以便于我们遍历所有的和然后去找我们所要的值,三个没有顺序的数或者变量我们可以以固定遍历中间的数为抓手,算法的一个常用的思维就是找中间的数以及其他形式的内容将问题一分为二。例如本题:我们抓住中间的数组将其固定下来,然后前后即一分为二分为前缀后缀,我们即可利用前缀后缀性质降低时间复杂度,解得问题。
#include<stdio.h>
#define N 100100
int a[N],b[N],c[N];
int la[N],lb[N],lc[N];
int ra[N],rb[N],rc[N];
int max(int b,int c)
{ int m=b;
if(c>b)
m=c;
return m;
}
int main()
{
int t;
int n;
scanf("%d",&t);
for(int i=1;i<=t;i++)
{
scanf("%d",&n);
for(int j=1;j<=n;j++) scanf("%d",&a[j]);
for(int j=1;j<=n;j++) scanf("%d",&b[j]);
for(int j=1;j<=n;j++) scanf("%d",&c[j]);
ra[0]=rb[0]=rc[0]=0;
ra[n+1]=rb[n+1]=rc[n+1]=0;
for(int i=1;i<=n;i++)
{
la[i]=max(la[i-1],a[i]);
lb[i]=max(lb[i-1],b[i]);
lc[i]=max(lc[i-1],c[i]);
}
for(int i=n;i>=1;i--)
{
ra[i]=max(ra[i+1],a[i]);
rb[i]=max(rb[i+1],b[i]);
rc[i]=max(rc[i+1],c[i]);
}
int ans=0;
int maxa,maxb,maxc;
for(int j=2;j<n;j++)
{
ans=max(lb[j-1]+a[j]+rc[j+1],ans);
ans=max(lc[j-1]+a[j]+rb[j+1],ans);
ans=max(la[j-1]+b[j]+rc[j+1],ans);
ans=max(lc[j-1]+b[j]+ra[j+1],ans);
ans=max(la[j-1]+c[j]+rb[j+1],ans);
ans=max(lb[j-1]+c[j]+ra[j+1],ans);
}
printf("%d\n",ans);
}
return 0;
}
方法2:先找到每个数组前三大的三个数,记录其下标。因为我们实际上我们需要的最大值就在这3组数中,因为最大值就在这三组数中相加中,我们先缩小我们的数据范围到三乘三。记录的下标是为了确认他们不在同一列,即得最大值。
#include<stdio.h>
#define N 100010
int a[N],b[N],c[N];
int main()
{
int t;
int n;
scanf("%d",&t);
for(int i=1;i<=t;i++)
{int amax[3]={0};int flaga=0;int numa[3]={0};
int bmax[3]={0};int flagb=0;int numb[3]={0};
int cmax[3]={0};int flagc=0;int numc[3]={0};
scanf("%d",&n);
for(int j=1;j<=n;j++) scanf("%d",&a[j]);
for(int j=1;j<=n;j++) scanf("%d",&b[j]);
for(int j=1;j<=n;j++) scanf("%d",&c[j]);
for(int j=1;j<n;j++)
{
for(int k=j;k<n;k++)
{
if(a[k]>=a[k+1])
{
int t ;
t=a[k];
a[k]=a[k+1];
a[k+1]=t;
}
}
}
int max=0;
for(int i=0;i<3;i++)
{
for(int j=0;j<3;j++)
{
for(int k=0;k<3;k++)
{
if((numa[i]!=numb[j])&&(numc[k]!=numb[j])&&(numa[i]!=numc[k]))
{
if(max<(amax[i]+bmax[j]+cmax[k]))
{
max=amax[i]+bmax[j]+cmax[k];
}
}
}
}
}
printf("%d\n",max);
}
return 0;
}
D(补题)
做完C题睡不着觉满脑子就是D题,因为D题的思路也是利用维护前后缀与前后缀和的性质,第二天中午放弃午睡做出,还挺兴奋的(乐),这可能就是属于萌新の快乐吧。
思路:我们从答案的角度切题,我们要将所有符合条件可能的情况都遍历一遍,然后找到其中的最大值。首先阅读条件,将条件解析转换成计算的方法(我们单看一种情况然后分析每种情况的共性也就是普遍规律来总结方法),然后看方法可不可以在相应的复杂度中将所有可能的情况都遍历一遍。
方法:我们选中一个情况去解析,会发现所有的情况都是上面a[i]的前缀相加+b[i]的前缀最大值,即为每种情况都取得最大值的方法,然后我们根据方法去遍历所有的情况,即求出最大值。
前缀和以及维护前缀的方法将原本o[n^2]的时间复杂度降至了o[n],极大的降低了时间复杂度,提高了运行效率。再就是别忘了讨论k>n与k<n的两种不同的遍历情况,第一遍忘记了k<n的情况,仍然遍历了n次,导致出错。
#include<stdio.h>
#define N 200010
int a[N],b[N];
int asum[N],bmax[N];
int max(int a,int b)
{
int maxx=a;
if(b>a) maxx=b;
return maxx;
}
int main()
{
int t;
scanf("%d",&t);
int n,k;
for(int i=0;i<t;i++)
{
scanf("%d %d",&n,&k);
for(int j=1;j<=n;j++) scanf("%d",&a[j]);
for(int j=1;j<=n;j++) scanf("%d",&b[j]);
asum[1]=a[1];bmax[1]=b[1];
for(int j=1;j<n;j++)
{
bmax[j+1]=max(bmax[j],b[j+1]);
}
for(int j=1;j<n;j++)
{
asum[j+1]=asum[j]+a[j+1];
}
int sum;
int maxx=0;
if(k>n)
{
for(int j=1;j<=n;j++)
{
sum=asum[j]+(k-j)*bmax[j];
maxx=max(sum,maxx);
}
}
else
{
for(int j=1;j<=k;j++)
{
sum=asum[j]+(k-j)*bmax[j];
maxx=max(sum,maxx);
}
}
printf("%d\n",maxx);
}
return 0;
}
E1(补题)
#include<stdio.h>
long long a[10],b[10];
long long aa[10],bb[10];
long long aaa[10],bbb[10];
int main()
{
int t;
int n;
scanf("%d",&t);
for(int i=0;i<t;i++)
{
scanf("%d",&n);
for(int j=1;j<=n;j++)
{
scanf("%d",&a[j]);
}
for(int j=1;j<=n;j++)
{
scanf("%d",&b[j]);
}
for(int j=1;j<=n;j++)
{
aa[j]=(a[j]-1)+b[j];
bb[j]=(b[j]-1)+a[j];
aaa[j]=a[j]-1;
bbb[j]=b[j]-1;
}
long long turn=1;
long long maxa=0,maxb=0;
long long suma=0,sumb=0;
long long max=0;
for(int k=1;k<=n;k++)
{
max=0;
if(turn==1)
{
for(int j=1;j<=n;j++)
{
if(aa[j]>=max)
{
max=aa[j];
maxa=j;
}
}
}
else
{
for(int j=1;j<=n;j++)
{
if(bb[j]>=max)
{
max=bb[j];
maxb=j;
}
}
}
if(turn==1)
{
suma=(long long)suma+aaa[maxa];
aa[maxa]=0;
bb[maxa]=0;
}
else {
sumb=sumb+bbb[maxb];
aa[maxb]=0;
bb[maxb]=0;
}
turn=-turn;
}
printf("%lld\n",(suma-sumb));
}
return 0;
}
E2(补题)
#include<bits/stdc++.h>
using namespace std;
#define N 200010
int a[N],b[N];
long long aa[N],bb[N];
long long aaa[N],bbb[N];
long long maxxa[N],maxxb[N];
struct M{
long long aa;
long long bb;
long long ii;
}ab[N],ba[N];
bool cmp(M a,M b)
{
return a.aa>b.aa;
}
bool cmpp(M a,M b)
{
return a.bb>b.bb;
}
int max(int b,int c,int j,int max)
{ int m=b;
if(c>b)
m=c;
return m;
}
int main()
{
int t;
int n;
scanf("%d",&t);
for(int i=0;i<t;i++)
{
scanf("%d",&n);
for(int j=1;j<=n;j++)
{
scanf("%d",&a[j]);
}
for(int j=1;j<=n;j++)
{
scanf("%d",&b[j]);
}
for(int j=1;j<=n;j++)
{
ab[j].aa=(a[j]-1)+b[j];
ba[j].bb=(b[j]-1)+a[j];
aaa[j]=a[j]-1;
bbb[j]=b[j]-1;
}
for(int j=1;j<=n;j++)
{
ab[j].ii=j;
ba[j].ii=j;
}
sort(ab+1,ab+n+1,cmp);
sort(ba+1,ba+n+1,cmpp);
long long maxa=0,maxb=0;
long long suma=0,sumb=0;
for(int j=n;j>=1;j--)
{
if(j%2==1)
{
suma=suma+aaa[ab[j].ii];
}
else
{
sumb=sumb+bbb[ba[j].ii];
}
}
printf("%lld\n",(suma-sumb));
}
return 0;
}