信息学奥赛一本通 2077:【21CSPJ普及组】小熊的果篮(fruit) | 洛谷 P7912 [CSP-J 2021] 小熊的果篮

本文主要是介绍信息学奥赛一本通 2077:【21CSPJ普及组】小熊的果篮(fruit) | 洛谷 P7912 [CSP-J 2021] 小熊的果篮,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

【题目链接】

信息学奥赛一本通 2077:【21CSPJ普及组】小熊的果篮(fruit)
洛谷 P7912 [CSP-J 2021] 小熊的果篮

【题目考点】

1. 链表
2. stl list
3. stl set

【解题思路】

解法1:链表中包含链表

创建一个block双向链表,block链表中的每个元素都是一个“块”。
(之所以建双向链表,是因为下面有在block链表中删除结点的操作,双向链表删除写起来相对比较容易。如果使用单链表也可以完成该题。)
每个“块”也是一个单向链表,叫做fruit链表,保存这一个块中每个水果的编号。
首先根据读入的序列,创建block链表。

  • 如果当前读入数字(水果编号)和上个数字相同,则把当前数字加到当前fruit链表之中。
  • 否则在block链表中用尾插法添加一个元素(fruit链表),新的fruit链表添加水果编号

每次遍历block链表,在block链表中的每个“块”中(fruit链表)取第一个数字输出,并删除fruit链表中的第一个数字。
如果当前fruit链表在删除数字后,成为空链表,那么需要在block链表中删除该空链表,并判断前一个链表和后一个链表的水果种类是否相同,如果相同,则合并两个单链表(即把后一个链表接到前一个链表的后面)。

针对该解法可以自己写链表,也可以使用STL list类来完成。

解法2:使用两个set

set的原理是红黑树,可以维护一个有序序列,可以使用lower_bound或upper_bound成员函数以 O ( l o g n ) O(logn) O(logn)复杂度找到其中符合某一条件的元素。
设两个set:st0与st1。st0:保存种类为0的水果的编号 st1:保存种类为1的水果的编号
每次循环,从st0与st1哪个集合的最小值更小,就从哪个集合开始。交替在st0与st1中选择数字,下一次在另一个集合中选择出的数字要比上一次选择出的数字更大,并删除数字。直到在另一个集合中选择出的数字没有比上一次选择出的数字更大,则跳出循环。

由于set使用lower_bound的复杂度是 O ( l o g n ) O(logn) O(logn),该过程需要做n次,总体复杂度为 O ( n l o g n ) O(nlogn) O(nlogn),是可以接受的。

【题解代码】

解法1:链表中包含链表
  • 写法1:自己写链表
#include <bits/stdc++.h>
using namespace std;
#define N 200005
struct Fruit//fruit链表中结点类型 
{int num;//num:水果编号 int next;//单链表下一个结点的地址 
};
Fruit fruit[2*N];//由于有头结点,总结点数多于N 
int fp, ft;
int TEST;
struct Block//block链表中结点类型 
{int type, head, tail;//type:水果种类  head:头指针 tail:尾指针 int pre, next;//pre, next:双向链表中前一个结点及下一个结点的地址int front(){return fruit[fruit[head].next].num;}void push_back(int num){fruit[++fp].num = num;fruit[tail].next = fp;tail = fp;}void pop_front(){head = fruit[head].next;}bool empty(){return head == tail;}void merge(Block bk)//当前链表末尾连接新链表 新链表为bk {fruit[tail].next = fruit[bk.head].next;tail = bk.tail;} 
};
Block block[N];
int bp, bhead = ++bp, btail = bhead;
void block_push_back(int type, int fruitNum)//type:水果类型 fruitNum:第一个水果编号 
{block[++bp].type = type;block[bp].head = block[bp].tail = ++fp;//在添加结点时,才给该block内的fruit链表设头结点 block[bp].push_back(fruitNum);block[bp].pre = btail;block[btail].next = bp;btail = bp;
}
bool block_empty()
{return bhead == btail;
}
void block_erase(int ep)//删掉地址为ep的结点 
{block[block[ep].pre].next = block[ep].next;block[block[ep].next].pre = block[ep].pre;if(btail == ep)btail = block[ep].pre;
}
int main()
{int n, a, lastNum = -1;//ln:上一次读入的数 cin >> n;for(int i = 1; i <= n; ++i){cin >> a; if(a == lastNum)block[btail].push_back(i);else{block_push_back(a, i);lastNum = a;}}while(block_empty() == false)//block链表不空 {for(int i = block[bhead].next; i != 0; i = block[i].next){//取出每块第一个水果,并删除水果的Fruit结点 cout << block[i].front() << ' ';block[i].pop_front();}cout << endl;for(int i = block[bhead].next; i != 0; i = block[i].next)//删除没有水果的块 if(block[i].empty())block_erase(i);int i = block[bhead].next;while(block[i].next != 0)//合并块i,nx{ int nx = block[i].next;if(block[i].type == block[nx].type){block[i].merge(block[nx]);block_erase(nx);}elsei = block[i].next;}}return 0;
}
  • 写法2:使用stl list
#include <bits/stdc++.h>
using namespace std;
#define N 200005
typedef list<list<int> >::iterator BlockIt;
typedef list<int>::iterator It;
list<list<int> > block;
int f[N];//f[i]:第i个水果是什么 
int main()
{int n, a, lastNum = -1;//ln:上一次读入的数 cin >> n;for(int i = 1; i <= n; ++i){cin >> a;f[i] = a;if(a == lastNum)prev(block.end())->push_back(i);//block最后元素添加i else{list<int> fruit;fruit.push_back(i);block.push_back(fruit);lastNum = a;}}while(block.empty() == false){BlockIt bi; for(bi = block.begin(); bi != block.end(); ++bi){//输出头 cout << bi->front() << ' ';bi->pop_front();}cout << endl;bi = block.begin();while(bi != block.end()){//删除 if(bi->empty())bi = block.erase(bi);//删掉bi指向的结点,并让bi指向下一个结点 else++bi;}bi = block.begin(); while(next(bi) != block.end()){BlockIt pi = bi++;//pi在前,bi在后,判断pi,bi指向的结点是否可以合并 if(f[*pi->begin()] == f[*bi->begin()]){//类型相同, 可以合并 pi->splice(pi->end(), *bi);block.erase(bi);bi = pi;}}}return 0;
} 
解法2:使用两个set
#include <bits/stdc++.h>
using namespace std;
#define N 200005
set<int> st[2];//st[0]:保存种类为0的水果的编号 st[1]:保存种类为1的水果的编号 
int main()
{int n, a, p, lastNum; cin >> n;for(int i = 1; i <= n; ++i){cin >> a;if(a == 0)st[0].insert(i);elsest[1].insert(i);}while(!(st[0].empty() && st[1].empty())){if(st[0].empty())//确定p中起始下标位置 p = 1;else if(st[1].empty())p = 0;else if(*st[0].begin() > *st[1].begin())p = 1;elsep = 0;lastNum = -1;while(true){set<int>::iterator it = st[p].lower_bound(lastNum); if(it == st[p].end())break;cout << *it << ' ';lastNum = *it;st[p].erase(it);p = (p+1)%2;//p从0变为1,1变为0; }cout << endl; }return 0;
} 

这篇关于信息学奥赛一本通 2077:【21CSPJ普及组】小熊的果篮(fruit) | 洛谷 P7912 [CSP-J 2021] 小熊的果篮的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/178520

相关文章

CSP 2023 提高级第一轮 CSP-S 2023初试题 完善程序第二题解析 未完

一、题目阅读 (最大值之和)给定整数序列 a0,⋯,an−1,求该序列所有非空连续子序列的最大值之和。上述参数满足 1≤n≤105 和 1≤ai≤108。 一个序列的非空连续子序列可以用两个下标 ll 和 rr(其中0≤l≤r<n0≤l≤r<n)表示,对应的序列为 al,al+1,⋯,ar​。两个非空连续子序列不同,当且仅当下标不同。 例如,当原序列为 [1,2,1,2] 时,要计算子序列 [

生信代码入门:从零开始掌握生物信息学编程技能

少走弯路,高效分析;了解生信云,访问 【生信圆桌x生信专用云服务器】 : www.tebteb.cc 介绍 生物信息学是一个高度跨学科的领域,结合了生物学、计算机科学和统计学。随着高通量测序技术的发展,海量的生物数据需要通过编程来进行处理和分析。因此,掌握生信编程技能,成为每一个生物信息学研究者的必备能力。 生信代码入门,旨在帮助初学者从零开始学习生物信息学中的编程基础。通过学习常用

生信圆桌x生信分析平台:助力生物信息学研究的综合工具

介绍 少走弯路,高效分析;了解生信云,访问 【生信圆桌x生信专用云服务器】 : www.tebteb.cc 生物信息学的迅速发展催生了众多生信分析平台,这些平台通过集成各种生物信息学工具和算法,极大地简化了数据处理和分析流程,使研究人员能够更高效地从海量生物数据中提取有价值的信息。这些平台通常具备友好的用户界面和强大的计算能力,支持不同类型的生物数据分析,如基因组、转录组、蛋白质组等。

CSP-J基础之数学基础 初等数论 一篇搞懂(一)

文章目录 前言声明初等数论是什么初等数论历史1. **古代时期**2. **中世纪时期**3. **文艺复兴与近代**4. **现代时期** 整数的整除性约数什么样的整数除什么样的整数才能得到整数?条件:举例说明:一般化: 判断两个数能否被整除 因数与倍数质数与复合数使用开根号法判定质数哥德巴赫猜想最大公因数与辗转相除法计算最大公因数的常用方法:举几个例子:例子 1: 计算 12 和 18

GPU 计算 CMPS224 2021 学习笔记 02

并行类型 (1)任务并行 (2)数据并行 CPU & GPU CPU和GPU拥有相互独立的内存空间,需要在两者之间相互传输数据。 (1)分配GPU内存 (2)将CPU上的数据复制到GPU上 (3)在GPU上对数据进行计算操作 (4)将计算结果从GPU复制到CPU上 (5)释放GPU内存 CUDA内存管理API (1)分配内存 cudaErro

2021-8-14 react笔记-2 创建组件 基本用法

1、目录解析 public中的index.html为入口文件 src目录中文件很乱,先整理文件夹。 新建components 放组件 新建assets放资源   ->/images      ->/css 把乱的文件放进去  修改App.js 根组件和index.js入口文件中的引入路径 2、新建组件 在components文件夹中新建[Name].js文件 //组件名首字母大写

2021-08-14 react笔记-1 安装、环境搭建、创建项目

1、环境 1、安装nodejs 2.安装react脚手架工具 //  cnpm install -g create-react-app 全局安装 2、创建项目 create-react-app [项目名称] 3、运行项目 npm strat  //cd到项目文件夹    进入这个页面  代表运行成功  4、打包 npm run build

CSP-J基础之数学基础 初等数论 一篇搞懂(二)

文章目录 前言算术基本定理简介什么是质数?举个简单例子:重要的结论:算术基本定理公式解释:举例: 算术基本定理的求法如何找出质因数:举个简单的例子: 重要的步骤:C++实现 同余举个例子:同余的性质简介1. 同余的自反性2. 同余的对称性3. 同余的传递性4. 同余的加法性质5. 同余的乘法性质 推论 总结 前言 在计算机科学和数学中,初等数论是一个重要的基础领域,涉及到整数

CSP-J基础之cmath常见函数

文章目录 前言1. **`sin` 函数**2. **`cos` 函数**3. **`exp` 函数**4. **`log` 函数**5. **`fabs` 函数**6. **`pow` 函数**7. **`sqrt` 函数**8. **`ceil` 函数**9. **`floor` 函数** 总结 前言 在计算机科学与编程中,数学函数是解决各种计算问题的基础工具。C++标准

【生物信息学算法】图算法1:概念和算法

文章目录 1. 图的定义、分类、表达方式图的定义图的分类表达方式Python实现 2.相邻节点和度概念定义python实现 3.路径、距离和搜索路径和距离搜索环 4.图论中的欧拉定理 1. 图的定义、分类、表达方式 图的定义 图G可以由两个集合来定义,即G=(V,E)。其中,V是对象的集合,称为图的顶点或节点; E是V中(u,v)顶点对的集合,称为边或弧,表示u和v之间的关系