IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 新鲜出炉的 CSP-J 2021 复赛题目 题解 -> 正文阅读

[数据结构与算法]新鲜出炉的 CSP-J 2021 复赛题目 题解

2021普及组复赛

代码量大点,难度一般。

T1 P7909 [CSP-J 2021] 分糖果

最简单容易想到的思路,应该就是枚举[L,R]范围内的每一个数字 %n 的结果,保留最大结果,如下所示。

//分糖果 
#include<bits/stdc++.h>
using namespace std;
int main(){
	int n,L,MX=0;
	cin>>n>>L>>R;
	for(int i=L;i<=R;i++)
	{
		MX=max(MX,i%n);	
	}	
	cout<<MX;
	return 0;
}

有的同学可能还会优化一下,因为MX最大为 n-1

//分糖果 
#include<bits/stdc++.h>
using namespace std;
int main(){
	int n,L,MX=0;
	cin>>n>>L>>R;
	for(int i=L;i<=R;i++)
	{
		MX=max(MX,i%n);	
        if(Mx==n-1)
            break;
	}	
	cout<<MX;
	return 0;
}

但是这两种方法都不是满分算法,时间复杂度分别为 o(R-L) 和 o(n) ,

结合数据来看的话,都是70分.

继续分析一下

实际问题就是要求 k % n 的最大值, k在 [L,R]范围内.

k % n 的结果,在[0,n-1]范围内。

随着k从L增加R,结果的变化 可以分为两种情况,

达到过 n-1 ,然后到 0 , 然后再继续增加。这种情况答案就是n-1

触顶过

一直增加,这个时候k取R最优

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-94PfyM6e-1635430011173)(NOIP%E7%9C%9F%E9%A2%98.assets/image-20211028211819828.png)]单调递增

那么 我们只需要判断 k % n 能不能增加到 n-1。

能的话结果是n-1,不能的话结果是R%n。

#include<bits/stdc++.h>
using namespace std;
int main(){
	int n,L,R,t,MX=0;
	cin>>n>>L>>R;
	t=L%n;
	if(t+(R-L)>=n-1)
		cout<<n-1;
	else
		cout<<R%n;
	return 0;
}

这样o(1)的算法,就可以满分了

T2 P7910 [CSP-J 2021] 插入排序

这个题目可以模拟来做,也可以用树状数组。

考虑到普及组的同学学过树状数组的可能不太多,这里介绍模拟的做法。

这里设计到查询查询元素的原位置,所以用结构体存储值和原来的位置。

题目中有至多5000次的单点修改,每次修改完直接sort()排序时,复杂度为5000 * n * logn,会超时。

熟悉插入排序的话,会发现排序后的元素修改单个元素后的o(n)的时间内就可以完成调整使数组重新有序。单点修改的操作计算量可以优化为 5000 * n.

再结合数据范围来看,查询的次数比较多,而且查询的是原来某个元素排序后的位置,而且这种查询次数在 105 这个级别,所以我们可以把这个数据提前存储下来,从而实现 o(1) 的查询。

这样的话总计算量是 n* logn + 5000 * n + q,结合数据范围看,可以AC。

//插入排序 
/*

解题思路:
 利用结构体存储每个数字的值和它原来的位置  然后直接进行插入排序
 并用 cha数组 记录元素排序后的位置 
 当需要修改元素时:先找到元素所在位置 修改元素的值 并调整元素的位置 向前或者向后调整 o(n)
 查询:直接根据 cha[] 查询 o(1)
  
*/
#include<bits/stdc++.h>
using namespace std;
struct NUM{
 int val,pos;
}num[8010];
int n,cha[8010];//cha 数组方便查询 记录一下原来的第x个元素 现在 在哪个位置 
bool cmp(NUM a,NUM b)
{
	if(a.val<b.val)
		return 1;
	else if(a.val==b.val&&a.pos<b.pos)
		return 1;
	else
		return 0;
}
void gai(int x,int v)//把原来的 第x个元素改成v 然后调整数组元素顺序  
{
	num[cha[x]].val=v;
	
	int i=cha[x]; 
	//接下来可能需要向前调整顺序或者向后调整

	while(!cmp(num[i-1],num[i])&&i>1)	//向前调整
	{
		swap(num[i-1],num[i]);
		i--;
	} 
	
	while(!cmp(num[i],num[i+1])&&i<n)//向后调整 
	{
		swap(num[i],num[i+1]);
		i++;
	} 
 
	for(i=1;i<=n;i++)//记录下原来的第x的元素现在到哪了 
	{
		cha[num[i].pos]=i;
	}
 
}

int main(){
	int Q;
	cin>>n>>Q;
	for(int i=1;i<=n;i++)
	{
		cin>>num[i].val;
		num[i].pos=i;
	}
	sort(num+1,num+1+n,cmp);
	
	for(int i=1;i<=n;i++)//记录下原来的第x的元素现在到哪了 
	{
		cha[num[i].pos]=i;//原来的第pos个元素在下标 i 处 
	}
	while(Q--)
	{
		int q,x,v;
		cin>>q;
		if(q==1)
		{
			cin>>x>>v;
			gai(x,v);
		}
		else
		{
			cin>>x;
			cout<<cha[x]<<endl; 
		}
	}

	return 0;
}

T3 P7911 [CSP-J 2021] 网络连接

这也是一道模拟题。

核心在于验证一个IP地址的合法性。

细心验证题目里出现的各个问题即可。

同学们在做的时候除了单独验证某个问题外,还要注意自己的解题方法,会不会因为几个问题叠加在一起,会产生新的问题。

我的代码写法中就有一个 因为两个冒号相连,从而会有一个空数字,调了好久…

//P7911 [CSP-J 2021] 网络连接

/*
解题思路:模拟
	check()检查一个ip是否合法

*/
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
map<string,LL>mp;

char s[10],ip[100];
bool check(string a)//检查ip是否合法 
{
	string op,num;
	vector<string>all_num;
	for(int i=0;i<a.length();i++)//提取字符和数字 
	{
		if(ip[i]=='.'||ip[i]==':')
		{
			op.push_back(ip[i]);
			all_num.push_back(num);
			num.clear();	
		}
		else
		{
			num.push_back(ip[i]);
		}
		
	}
	all_num.push_back(num);//最后一个数字 单独存一下 
	num.clear();
	
	if(op=="...:")//字符顺序对 再检验数字 
	{
		for(int i=0;i<4;i++)
		{
			if(all_num[i].size()==0)//可能出现两个符号连着 从而出现空数字的情况 
				return 0;
			if(all_num[i].size()>1&&all_num[i][0]=='0')//含前导0 不合法 
				return 0;
			if(all_num[i].length()>3||(all_num[i].length()==3&&all_num[i]>"255")) 
				return 0;
		}
		if(all_num[4].length()>5 ||(all_num[4].length()==5&&all_num[4]>"65535")) 
			return 0;
		return 1;
	}
	else	//字符顺序不对 ip不合法 
	{
		return 0;
	}
	
}
int main(){
	
	int n;
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		scanf("%s %s",s,ip);
		if(!check(ip))
		{
			puts("ERR");
			continue;
		}
		if(s[0]=='S')
		{
			if(!mp[ip])
			{
				mp[ip]=i;
				puts("OK");
			}
			else
			{
				puts("FAIL");
			}
		}
		else
		{
			if(mp[ip]!=0)
				printf("%d\n",mp[ip]);
			else
				puts("FAIL");
		}
	}
	
	return 0;
}

T4 P7912 [CSP-J 2021] 小熊的果篮

还是模拟…

用双链表数据结构存储所有节点,然后单独存一下每个块的第一个元素编号。

每次输出一个块的头元素并将其删除,同时存储新块的开头的元素,这是一个o(n)的算法,下面直接看代码。

//小熊的果篮 
#include<bits/stdc++.h>
using namespace std;
const int N=200010;
int a[N],L[N],R[N]; 
int main(){
	int n;
	scanf("%d",&n);
	vector<int>b;
	for(int i=1;i<=n;i++)//初始化双链表 存储每个块的头元素 位置 
	{
		scanf("%d",&a[i]);
		if(i==1||a[i]!=a[i-1])
			b.push_back(i);
		L[i]=i-1;
		R[i]=i+1;
	}
	R[0]=1,L[n+1]=n;
	a[0]=a[n+1]=-1;
	
	while(R[0]!=n+1)//当双链表不空 
	{
		vector<int>c;
		for(auto u:b)
		{
			printf("%d ",u);//输出每个块的头结点 
			
			int x=L[u],y=R[u]; //删除已输出的 节点u 
			R[x]=y,L[y]=x;	
			
			if(a[u]==a[y]&&a[u]!=a[x])c.push_back(y);//u和左元素不同且和右元素相同 则是块的起点 就存下来 
		}
		puts("");
		swap(b,c);
	}
	return 0;
}

总体来说难度不高,居然没有dp的题目。

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2021-10-29 13:18:32  更:2021-10-29 13:21:04 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 9:28:50-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码