2021CSP-J题解

【CSP-J 2021】1.分糖果

此题考查的主要是数学归纳能力,与前几年套路一样,都是先水后难
整合题意:在[L,R]内找到一个x,使x%n最大

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int n,l,r,i,maxx=0,k;
	scanf("%d %d %d",&n,&l,&r);
	k=r/n*n-1;
	if(k>=l)
		printf("%d",k%n);
	else if(r/n==1)
		printf("%d",r%n);
	else
	{
		for(i=k+1;i<=r;i++)
			maxx=max(maxx,i%n);
		printf("%d",maxx);	
	}
	return 0;
}

【CSP-J 2021】2.插入排序

核心知识点:双向索引,对排序的理解,对时间复杂度的理解
用时间复杂度来分析题目,查询上限200000,n上限8000,常规做法每轮查询都排序时间复杂度200000* 8000* 13,肯定时间爆掉了,
再看修改数据5000,5000* 8000复杂度是跑不掉的,那外面也套不了什么优化了,所以这题时间复杂度就是5000* 8000+(200000-5000),命令2是O(1)
这题就是用rank和sa数组
具体做法
1.读入数据,排序,生成rank和sa数组
2.碰到2命令直接输出,碰到1命令,修改arr中的值,然后判断是向前做一趟排序,还是向后做一趟排序,同时改变sa和rank的值,change函数实现

注:sa[i]=j表示排序后的第i个元素在原数组的第j个,rk[i]=j表示原数组的第i个元素在排序后的第j个

#include<bits/stdc++.h>
using namespace std;
typedef struct node
{
	int id,v;
	bool operator < (const struct node & t)const
	{
		if(v==t.v)
			return id<t.id;
		return v<t.v;
	}
}NODE,*PNODE;
NODE arr[8005];
int sa[8005],rk[8005],n;
void change(int id,int v)
{
	int k=rk[id];
	bool s;
	if(v==arr[k].v)//不变就直接返回 
		return;
	s=arr[k].v>v;//s用来判断是比原值大了还是小了 
	arr[k].v=v;
	if(s)
	{
		for(;k>=1&&arr[k]<arr[k-1];k--)
		{
			swap(arr[k],arr[k-1]);
			swap(rk[sa[k]],rk[sa[k-1]]);
			swap(sa[k],sa[k-1]);
		}
	}
	else
	{
		for(;k<n&&arr[k+1]<arr[k];k++)
		{
			swap(arr[k],arr[k+1]);
			swap(rk[sa[k]],rk[sa[k+1]]);
			swap(sa[k],sa[k+1]);
		}
	}
}
int main()
{
	int i,q,a,b,tt;
	scanf("%d %d",&n,&q);
	for(i=1;i<=n;i++)
	{
		scanf("%d",&arr[i].v);
		arr[i].id=i;	
	}
	sort(arr+1,arr+1+n);
	for(i=1;i<=n;i++)
	{
		sa[i]=arr[i].id;
		rk[arr[i].id]=i;	
	}
	while(q--)
	{
		scanf("%d",&tt);
		if(tt==2)
		{
			scanf("%d",&a);
			printf("%d\n",rk[a]);
		}
		else if(tt==1)
		{
			scanf("%d %d",&a,&b);
			change(a,b);	
		}
	}
	return 0;
}

【CSP-J 2021】3.网络连接

核心知识点:字符串处理、map运用、string、基础代码能力
这题对学生综合素养要求比较高
用时间复杂度来分析题目,看数据规模这题就不是用来考时间复杂度的,就是考查字符串功底和数据结构的水平
具体做法如下:
1.把输入数据分两个字符串接收 cmd和adr
2.主要处理adr
(1)因为你不知道它数字符号怎么组,顺着验非常烦,不如把数字都取出来,不足或超过4个超范围都直接非法,
然后把四个数字拼成合法地址,回验adr合法性,这样还能带走前导0的问题
(2)利用map做到下标对位的功能,map中string直接对位地址,int可以记录合法编号

注:自己分析这题还是有些好处的,这里就提供一个框架

#include<bits/stdc++.h>
using namespace std;
char cmd[10],adr[30],tmp[30];
int arr[30],a_l=0;
map<string,int> mp;
int cnt;
void get_num()//字符串基本技巧,把数字读出来放入arr数组 
{
	
}
int check()
{
	/*
	.... 
	*/ 
	if()//判断是否合法 
	{
		memset(tmp,0,sizeof(tmp));
		sprintf(tmp,"%d.%d.%d.%d:%d",arr[0],arr[1],arr[2],arr[3],arr[4]);
		if(strcmp(tmp,adr)==0)//技巧,读出合法数量的数字,拼出合法字符串和原地址进行对比 
			return 1;
		else
			return 0;
	}
	else
	{
		return 0;
	}
} 
int main()
{
	int n;
	string str;
	scanf("%d",&n);
	while(n--)
	{
		scanf("%s %s",cmd,adr);
		cnt++;
		if()//验地址是否合法 
		{
			printf("ERR\n");
			continue;	
		}
		str=adr;
		if(cmd[0]=='S')
		{	
			//利用map来检验server合法性 
		}
		else if(cmd[0]=='C')
		{
			//利用map来检验client合法性 
		}
	}
	return 0;
}


【CSP-J 2021】4.小熊的果篮

核心知识点:队列,分块
用时间复杂度来分析题目,最大数据范围200000,看看题目也没啥好二分的,基本上时间开销就是线性访问每个数据,再加上外循环处理每轮的连续快,所以一定是用链表做删除
考试考的很基础,但是对基础代码的要求比较高
构造一个block的结构体,上来类似去重处理,每个块记录开始位置lt和结束位置rt,然后块号对齐下标
构造pre和nxt链表,使得处理过的点删除的时候达到O(1)时间复杂度
自己如果写循环控制会写炸,利用STL的deque大大简化处理
具体做法如下:
1.把数据读入arr,然后处理出block数组,初始化链表pre、nxt,把块号放入q[0](利用两个deque对倒)
2.从一个队列中把块一个个读出来处理,然后处理完的块放入新队列,为下一轮做准备
(1)从队列中把每个块的第一个元素输出,从链表中删除
(2)删完如果块空了,就不用处理了,如果块长度大于1,看一下是否和新队列中最后一个块的值一样,一样的就合并这两个块,不一样就把处理完的块加入新队列
3.交换队列重复上面处理方法

#include<bits/stdc++.h>
using namespace std;
typedef struct block
{
	int lt,rt;
}BLOCK,*PBLOCK;
BLOCK b[200005];
int b_l;
int pre[200005],nxt[200005];
int arr[200005];
deque<int> q[2]; 
int k=0;
int main()
{
	int n,i,j,flag=0,pos,tmp;
	scanf("%d",&n);
	for(i=1;i<=n;i++)
	{
		scanf("%d",&arr[i]);
		pre[i]=i-1;
		nxt[i]=i+1;
	} 
	b[++b_l].lt=1;
	for(i=2;i<=n;i++)
	{
		if(arr[i]!=arr[i-1])
		{
			b[b_l++].rt=i-1;
			b[b_l].lt=i;	
		}
		if(i==n)
			b[b_l].rt=n;
	}
	for(i=1;i<=b_l;i++)
		q[k].push_back(i);
	while(!q[k].empty())
	{
		flag=0;
		while(!q[k].empty())
		{
			tmp=q[k].front();
			q[k].pop_front();
			if(!flag)
			{
				printf("%d",b[tmp].lt);
				flag=1;
			}
			else
				printf(" %d",b[tmp].lt);
			pos=b[tmp].lt;
			pre[nxt[pos]]=pre[pos];
			nxt[pre[pos]]=nxt[pos]; 
			if(b[tmp].lt==b[tmp].rt)
				continue;
			b[tmp].lt=nxt[pos];
			if(!q[k^1].empty()&&arr[b[q[k^1].back()].rt]==arr[b[tmp].lt])
				b[q[k^1].back()].rt=b[tmp].rt;
			else
				q[k^1].push_back(tmp);
		}
		printf("\n");
		k^=1;
	}
	return 0;
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值