2021年CSP-J-T1-分糖果(candy)
题目描述
红太阳幼儿园的小朋友们开始分糖果啦!
红太阳幼儿园有 n 个小朋友,你是其中之一。保证 n ≥ 2。
有一天你在幼儿园的后花园里发现无穷多颗糖果,你打算拿一些糖果回去分给幼儿园的小朋友们。
由于你只是个平平无奇的幼儿园小朋友,所以你的体力有限,至多只能拿 R 块糖回去。
但是拿的太少不够分的,所以你至少要拿 L 块糖回去。保证 n ≤ L ≤ R。
也就是说,如果你拿了 k 块糖,那么你需要保证 L ≤ k ≤ R。
如果你拿了 k 块糖,你将把这 k 块糖放到篮子里,并要求大家按照如下方案分糖果:只要篮子里有 不少于 n 块糖果,幼儿园的所有 n 个小朋友(包括你自己)都从篮子中拿走恰好一块糖,直到篮子里的糖数量少于 n 块。此时篮子里剩余的糖果均归你所有——这些糖果是作为你搬糖果的奖励。
作为幼儿园高质量小朋友,你希望让作为你搬糖果的奖励的糖果数量(而不是你最后获得的总糖果数量!)尽可能多;因此你需要写一个程序,依次输入 n, L, R,并输出出你最多能获得多少作为你搬糖果的奖励的糖果数量。
输入格式
从文件 candy.in 中读入数据。
输入一行,包含三个正整数 n, L, R,分别表示小朋友的个数、糖果数量的下界和上界。
输出格式
输出到文件 candy.out 中。
输出一行一个整数,表示你最多能获得的作为你搬糖果的奖励的糖果数量。
输入输出样例
输入样例1:
7 16 23
输出样例1:
6
输入样例2:
10 14 18
输出样例2:
8
说明
【样例 1 解释】
拿 k = 20 块糖放入篮子里。
篮子里现在糖果数 20 ≥ n = 7,因此所有小朋友获得一块糖;
篮子里现在糖果数变成 13 ≥ n = 7,因此所有小朋友获得一块糖;
篮子里现在糖果数变成 6 < n = 7,因此这 6 块糖是作为你搬糖果的奖励。
容易发现,你获得的作为你搬糖果的奖励的糖果数量不可能超过 6 块(不然,篮子里的糖果数量最后仍然不少于 n,需要继续每个小朋友拿一块),因此答案是 6。
【样例 2 解释】
容易发现,当你拿的糖数量 k 满足 14 = L ≤ k ≤ R = 18 时,所有小朋友获得一块糖后,剩下的 k-10 块糖总是作为你搬糖果的奖励的糖果数量,因此拿 k = 18 块是最优解,答案是 8。
【数据范围说明】
对于所有数据,保证 2 ≤ n ≤ L ≤ R ≤ 10^9。
耗时限制1000ms 内存限制128MB
解析
考点:数论,枚举优化
参考代码:
#include<bits/stdc++.h>
using namespace std;
int main(){
int n, l, r, ans = 0;
cin >> n >> l >> r;
for(int k = l; k <= r; k++){
ans = max(ans, k % n);
}
cout << ans;
return 0;
}
正确的做法
![](https://img-blog.csdnimg.cn/direct/b8ca8b150a3f4e8b845bf7d8127a610c.png)
参考代码
#include <bits/stdc++.h>
using namespace std;
/*
给定 n,l,r,问[l,r] % n 最大值。
*/
int n,l,r;
int main(){
cin>>n>>l>>r;
if(l % n + r - l < n - 1) cout<<r % n;
else cout<<n - 1;
return 0;
}
2021年CSP-J-T2-插入排序(sort)
题目描述
插入排序是一种非常常见且简单的排序算法。小 Z 是一名大一的新生,今天 H 老师刚刚在上课的时候讲了插入排序算法。
假设比较两个元素的时间为 O(1),则插入排序可以以 O(n2) 的时间复杂度完成长度为 n 的数组的排序。不妨假设这 n 个数字分别存储在 a1, a2, · · · , an 之中,则如下伪代码给出了插入排序算法的一种最简单的实现方式:
为了帮助小 Z 更好的理解插入排序,小 Z 的老师 H 老师留下了这么一道家庭作业:
H 老师给了一个长度为 n 的数组 a,数组下标从 1 开始,并且数组中的所有元素均为非负整数。小 Z 需要支持在数组 a 上的 Q 次操作,操作共两种,参数分别如下:
1 x v : 这是第一种操作,会将 a 的第 x 个元素,也就是 ax 的值,修改为 v。保证1 ≤ x ≤ n, 1 ≤ v ≤ 10^9。注意这种操作会改变数组的元素,修改得到的数组会被保留,也会影响后续的操作
2 x : 这是第二种操作,假设 H 老师按照上面的伪代码对a数组进行排序,你需要告诉H老师原来a 的第 x 个元素,也就是 ax,在排序后的新数组所处的位置。保证1 ≤ x ≤ n。注意这种操作不会改变数组的元素,排序后的数组不会被保留,也不会影响后续的操作。
H 老师不喜欢过多的修改,所以他保证类型 1 的操作次数不超过 5000。 小 Z 没有学过计算机竞赛,因此小 Z 并不会做这道题。他找到了你来帮助他解决这个问题。
输入格式
从文件 sort.in 中读入数据。
输入的第一行包含两个正整数 n, Q,表示数组长度和操作次数。保证 1 ≤ n ≤ 8, 000, 1 ≤ Q ≤ 2 × 10^5。
输入的第二行包含 n 个空格分隔的非负整数,其中第 i 个非负整数表示 ai。保证1 ≤ ai ≤ 10^9。
接下来 Q 行,每行 2 ~ 3 个正整数,表示一次操作,操作格式见题目描述。
输出格式
输出到文件 sort.out 中。
对于每一次类型为 2 的询问,输出一行一个正整数表示答案。
输入输出样例
输入样例1:
3 4 3 2 1 2 3 1 3 2 2 2 2 3
输出样例1:
1 1 2
说明
【样例 1 解释】
在修改操作之前,假设 H 老师进行了一次插入排序,则原序列的三个元素在排序结束后所处的位置分别是 3, 2, 1。
在修改操作之前,假设 H 老师进行了一次插入排序,则原序列的三个元素在排序结束后所处的位置分别是 3, 1, 2。
注意虽然此时 a2 = a3,但是我们不能将能视为相同的元素
【数据范围】
对于所有测试数据,满足 1 ≤ n ≤ 8, 000, 1 ≤ Q ≤ 2*10^5, 1 ≤ x ≤ n, 1 ≤ v, ai ≤10^9。
对于所有测试数据,保证在所有 Q 次操作中,至多有 5000 次操作属于类型一。
各测试点的附加限制及分值如下表所示。
耗时限制1000ms 内存限制512MB
解析:
考点:排序
写法1:暴力sort()
每修改一次,就 stable_sort 排序一次(注意开结构体存原下标),然后每查询一次,就遍历找下标。很明显会超时,50 分。
写法2:不排序
原数修改,操作 2 只是问排序后排第几位,其实不用排序,只需查询数组中有多少个数小于它以及前面有多少个数等于它即可。70分。
参考代码
#include<bits/stdc++.h>
using namespace std;
const int N = 8e3 + 5;
int n, q, a[N];
int main() {
cin >> n >> q;
for(int i = 1; i <= n; i++) {
cin >> a[i];
}
while(q--) {
int x, y, z;
cin >> x;
if(x == 1) {
cin >> y >> z;
a[y] = z;
}
else {
cin >> y;
int ans = 0;
for(int i = 1; i < y; i++) {
if(a[i] <= a[y]) {
ans++;
}
}
for(int i = y + 1; i <= n; i++) {
if(a[i] < a[y]) {
ans++;
}
}
cout << ans + 1 << endl;
}
}
}
写法3:插入排序+标记优化
容易想到 sort 的思路,但是会超时。考虑优化:
优化就是如标题所示:插入排序。实际上只需要第一次先做一遍排序,然后后面,每次修改一个元素后,就只有两种可能:它变小了或变大了。
如果变小了,就把它用插入排序的思路,和左边的元素比,如果更小,就交换。如此反复,直至交换到它该在的位置(左边小于它,右边大于它)。为了提高查询效率,再定义一个数组标记原数的新位置即可。
如果变大了,同理,向右交换,直至交换到该在的位置。
这个过程是只需要交换最多 n 次,比 sort 排序要高效很多。
参考代码
#include <bits/stdc++.h>
using namespace std;
struct jd{
int bh,data;
}a[8010];
int n, q, b[8005];
bool cmp(jd a,jd b){
if(a.data!=b.data)return a.data<b.data;
return a.bh<b.bh;
}
void ops(int x, int v) {
if(a[b[x]].data < v) // 变大了,往后移
{
a[b[x]].data = v;
int pos = b[x];
while(pos+1 <= n && cmp(a[pos+1], a[pos]))
{
swap(a[pos], a[pos+1]);
swap(b[a[pos].bh], b[a[pos+1].bh]);
pos++;
}
} else { // 变小了,往左移
a[b[x]].data = v;
int pos = b[x];
while(pos-1 >= 1 && cmp(a[pos], a[pos-1]))
{
swap(a[pos], a[pos-1]);
swap(b[a[pos].bh], b[a[pos-1].bh]);
pos--;
}
}
}
int main(){
cin >> n >> q;
for(int i = 1; i <= n; i++){
cin >> a[i].data;
a[i].bh = i;
}
sort(a+1, a+n+1, cmp);
for(int i = 1; i <= n; i++) {
b[a[i].bh] = i;
}
int op, x, v;
while(q--){
cin >> op >> x;
if(op == 1) {
cin >> v;
ops(x, v);
} else {
cout << b[x] << '\n';
}
}
return 0;
}
2021年CSP-J-T3-网络连接(network)
题目描述
TCP/IP 协议是网络通信领域的一项重要协议。今天你的任务,就是尝试利用这个协议,还原一个简化后的网络连接场景。在本问题中,计算机分为两大类:服务机(Server)和客户机(Client)。服务机负责建立连接,客户机负责加入连接。需要进行网络连接的计算机共有 n 台,编号为 1 ~ n ,这些机器将按编号递增的顺序,依次发起一条建立连接或加入连接的操作。每台机器在尝试建立或加入连接时需要提供一个地址串。服务机提供的地址串表示它尝试建立连接的地址,客户机提供的地址串表示它尝试加入连接的地址。
一个符合规范的地址串应当具有以下特征:
1、必须形如 a.b.c.d:e 的格式,其中 a, b, c, d, e 均为非负整数;
2、0 ≤ a, b, c, d ≤ 255, 0 ≤ e ≤ 65535;
3、a, b, c, d, e 均不能含有多余的前导 0。
相应地,不符合规范的地址串可能具有以下特征:
1、不是形如 a.b.c.d:e 格式的字符串,例如含有多于 3 个字符 . 或多于 1 个字符 : 等情况;
2、整数 a, b, c, d, e 中某一个或多个超出上述范围;
3、整数 a, b, c, d, e 中某一个或多个含有多余的前导 0 。
例如,地址串 192.168.0.255:80 是符合规范的,但 192.168.0.999:80 、192.168.00.1:10、192.168.0.1:088 、192:168:0:1.233 均是不符合规范的。如果服务机或客户机在发起操作时提供的地址串不符合规范,这条操作将被直接忽
略。
在本问题中,我们假定凡是符合上述规范的地址串均可参与正常的连接,你无需考虑每个地址串的实际意义。由于网络阻塞等原因,不允许两台服务机使用相同的地址串,如果此类现象发生,后一台尝试建立连接的服务机将会无法成功建立连接;除此之外,凡是提供符合规范的地址串的服务机均可成功建立连接。如果某台提供符合规范的地址的客户机在尝试加入连接时,与先前某台已经成功建立连接的服务机提供的地址串相同,这台客户机就可以成功加入连接,并称其连接到这台服务机;如果找不到这样的服务机,则认为这台客户机无法成功加入连接。请注意,尽管不允许两台不同的服务机使用相同的地址串,但多台客户机使用同样的地址串,以及同一台服务机同时被多台客户机连接的情况是被允许的。你的任务很简单:在给出每台计算机的类型以及地址串之后,判断这台计算机的连接情况。
输入格式
从文件 network.in 中读入数据。
第 1 行,一个正整数 n 。
接下来 n 行,每行 2 个字符串 op, ad ,按照编号从小到大给出每台计算机的类型及地址串。
其中 op 保证为字符串 Server 或 Client 之一,ad 为一个长度不超过 25 的,仅由数字、字符 . 和字符 : 组成的非空字符串。每行的两个字符串之间用恰好一个空格分隔开,每行的末尾没有多余的空格。
输出格式
输出到文件 network.out 中。
输出共 n 行,每行一个正整数或字符串表示第 i 台计算机的连接状态。其中:
如果第 i 台计算机为服务机,则:
1. 如果其提供符合规范的地址串且成功建立连接,输出字符串 OK 。
2. 如果其提供符合规范的地址串,但由于先前有相同地址串的服务机而无法成功建立连接,输出字符串 FAIL 。
3. 如果其提供的地址串不是符合规范的地址串,输出字符串 ERR 。
如果第 i 台计算机为客户机,则:
1. 如果其提供符合规范的地址串且成功加入连接,输出一个正整数表示这台客户机连接到的服务机的编号。
2. 如果其提供符合规范的地址串,但无法成功加入连接时,输出字符串 FAIL 。
3. 如果其提供的地址串不是符合规范的地址串,输出字符串 ERR 。
输入输出样例
输入样例1:
5 Server 192.168.1.1:8080 Server 192.168.1.1:8080 Client 192.168.1.1:8080 Client 192.168.1.1:80 Client 192.168.1.1:99999
输出样例1:
OK FAIL 1 FAIL ERR
输入样例2:
10 Server 192.168.1.1:80 Client 192.168.1.1:80 Client 192.168.1.1:8080 Server 192.168.1.1:80 Server 192.168.1.1:8080 Server 192.168.1.999:0 Client 192.168.1.1.8080 Client 192.168.1.1:8080 Client 192.168.1.1:80 Client 192.168.1.999:0
输出样例2:
OK 1 FAIL FAIL OK ERR ERR 5 1 ERR
说明
【样例 1 解释】
计算机 1 为服务机,提供符合规范的地址串 192.168.1.1:8080 ,成功建立连接;
计算机 2 为服务机,提供与计算机 1 相同的地址串,未能成功建立连接;
计算机 3 为客户机,提供符合规范的地址串 192.168.1.1:8080 ,成功加入连接,并连接到服务机 1;
计算机 4 为客户机,提供符合规范的地址串 192.168.1.1:80 ,找不到服务机与其连接;
计算机 5 为客户机,提供的地址串 192.168.1.1:99999 不符合规范。
【数据范围】
“性质 1 ”为:保证所有的地址串均符合规范;
“性质 2 ”为:保证对于任意两台不同的计算机,如果它们同为服务机或者同为客户机,则它们提供的地址串一定不同;
“性质 3 ”为:保证任意一台服务机的编号都小于所有的客户机;
“性质 4 ”为:保证所有的地址串均形如 a.b.c.d:e 的格式,其中 a, b, c, d, e 均为
不超过 10^9 且不含有多余前导 0 的非负整数;
“性质 5 ”为:保证所有的地址串均形如 a.b.c.d:e 的格式,其中 a, b, c, d, e 均为只含有数字的非空字符串。
对于 100% 的数据,保证 1 ≤ n ≤ 1000 。
耗时限制1000ms 内存限制512MB
解析
考点:模拟,map
1.直接模拟
#include <bits/stdc++.h>
using namespace std;
const int N = 1010;
struct node{
string num;//成功建立连接的服务机的地址串
int pos;//服务机的编号
}a[N];
int k = 0;//表示成功建立连接服务机的数量
int n;
string op,ad;
//检验地址串的合法性
/*
检验 a.b.c.d:e 的格式,将所有的数字保留为 0,格式应该是:0.0.0.0:0
*/
bool check(string s){
if(s.size() > 21) return false;
string r = "";//保留格式
string w = "";//分解每个数字
int c = 0;//统计是第几个数字
for(int i = 0;i < s.size();i++){
if(isdigit(s[i])){
w = w + s[i];
//如果连续数字结束了
if(i==s.size()-1 || !isdigit(s[i+1])){
c++;//统计是第几个数字
if(c > 5) return false;//数字太多
if(w.size()>5) return false;//数字太大
if(w!="0"&&w[0]=='0') return false;//前导 0
int x = stoi(w);
//前 4 个数字
if(c <= 4 && (x > 255)) return false;
if(c == 5 && x > 65535) return false;//第 5 个数字
r = r + "0";
w = "";
}
}else{
r = r + s[i];//连接.或者:
}
}
if(r != "0.0.0.0:0") return false;
return true;//没有发现不符合规范的情况
}
//检验服务机能否成功建立连接
string server(string s,int pos){
//循环已经建立连接服务机
for(int i = 1;i <= k;i++){
if(a[i].num == s) return "FAIL";
}
//将服务机的信息存储下来
k++;
a[k].num = s;
a[k].pos = pos;
return "OK";
}
//检验客户机能否找到已经建立连接的服务机
int client(string s){
for(int i = 1;i <= k;i++){
if(a[i].num == s) return a[i].pos;
}
return -1;
}
int main(){
cin>>n;
for(int i = 1;i <= n;i++){
cin>>op>>ad;
//检验合法性
if(!check(ad)){
cout<<"ERR"<<endl;
continue;
}
//如果是服务机
if(op == "Server"){
cout<<server(ad,i)<<endl;//输出服务机启动情况
}else{
//客户机:如果不能成功连接,返回-1
int p = client(ad);
if(p == -1) cout<<"FAIL"<<endl;
else cout<<p<<endl;
}
}
return 0;
}
使用map
使用map
- 检查字符串是否是正常网络字符串,可以用stringstream。
- 用map存储服务端,客户端检测是否匹配。
- 也可以用sscanf来匹配网络字符串的个数,参考checknet2函数。
- 参考代码
#include<bits/stdc++.h> using namespace std; bool checknet(string s){ int a, b, c, d, port, cnt1 = 0, cnt2 = 0; char ch; for(int i = 0; i < s.size(); i++) { if(s[i] == '.') cnt1++; if(s[i] == ':') cnt2++; } if(cnt1 != 3 || cnt2 != 1) return 0; //确保.和:的个数正确 stringstream ss; ss << s; ss >> a >> ch >> b >> ch >> c >> ch >> d >> ch >> port; if(a<0||a>255||b<0||b>255||c<0||c>255||d<0||d>255||port<0||port>65535) //数字不匹配 return 0; ss.clear(), ss.str(""); //清空字符串流 ss << a << '.' << b << '.' << c << '.' << d << ':' << port; return ss.str() == s; //通过判断相等,检查格式和前导0 } bool checknet2(string s){ long long a, b, c, d, port; if (sscanf(s.c_str(), "%lld.%lld.%lld.%lld:%lld", &a, &b, &c, &d, &port) != 5) return 0; if (a < 0 || a > 255 || b < 0 || b > 255 || c < 0 || c > 255 || d < 0 || d > 255 || port < 0 || port > 65535) return 0; stringstream ss; ss << a << '.' << b << '.' << c << '.' << d << ':' << port; return ss.str() == s; } map<string, int> mp; int main(){ freopen("network.in", "r", stdin); freopen("network.out", "w", stdout); int n; string s1, s2; cin >> n; for(int i = 1; i <= n; i++) { cin >> s1 >> s2; if(!checknet(s2)) { cout << "ERR\n"; continue; } if(s1 == "Server"){ if(mp[s2]) cout << "FAIL\n"; else mp[s2] = i, cout << "OK\n"; }else{ if(mp.count(s2) == 0) cout << "FAIL\n"; else cout << mp[s2] << "\n"; } } return 0; }
2021年CSP-J-T4-小熊的果篮(fruit)
题目描述
小熊的水果店里摆放着一排 n 个水果。每个水果只可能是苹果或桔子,从左到右依次用正整数 1、2、3、……、n 编号。连续排在一起的同一种水果称为一个“块”。小熊要把这一排水果挑到若干个果篮里,具体方法是:每次都把每一个“块”中最左边的水果同时挑出,组成一个果篮。重复这一操作,直至水果用完。注意,每次挑完一个果篮后,“块”可能会发生变化。比如两个苹果“块”之间的唯一桔子被挑走后,两个苹果“块”就变成了一个“块”。请帮小熊计算每个果篮里包含的水果。
输入格式
从文件 fruit.in 中读入数据。
输入的第一行包含一个正整数 n,表示水果的数量。
输入的第二行包含 n 个空格分隔的整数,其中第 i 个数表示编号为 i 的水果的种
类,1 代表苹果,0 代表桔子。
输出格式
输出到文件 fruit.out 中。
输出若干行。
第 i 行表示第 i 次挑出的水果组成的果篮。从小到大排序输出该果篮中所有水果的
编号,每两个编号之间用一个空格分隔。
输入输出样例
输入样例1:
12 1 1 0 0 1 1 1 0 1 1 0 0
输出样例1:
1 3 5 8 9 11 2 4 6 12 7 10
输入样例2:
20 1 1 1 1 0 0 0 1 1 1 0 0 1 0 1 1 0 0 0 0
输出样例2:
1 5 8 11 13 14 15 17 2 6 9 12 16 18 3 7 10 19 4 20
说明
【样例 1 解释】
这是第一组数据的样例说明。
所有水果一开始的情况是 1 1 0 0 1 1 1 0 1 1 0 0,一共有 6 个块。
在第一次挑水果组成果篮的过程中,编号为 1 3 5 8 9 11 的水果被挑了出来。
之后剩下的水果是 1 0 1 1 1 0,一共 4 个块。
在第二次挑水果组成果篮的过程中,编号为 2 4 6 12 的水果被挑了出来。
之后剩下的水果是 1 1,只有 1 个块。
在第三次挑水果组成果篮的过程中,编号为 7 的水果被挑了出来。
最后剩下的水果是 1,只有 1 个块。
在第四次挑水果组成果篮的过程中,编号为 10 的水果被挑了出来。
【数据范围】
对于 10% 的数据,n ≤ 5。
对于 30% 的数据,n ≤ 1000。
对于 70% 的数据,n ≤ 50000。
对于 100% 的数据,n ≤ 2 × 105。
【提示】
由于数据规模较大,建议 C/C++ 选手使用 scanf 和 printf 语句输入、输出。
耗时限制1000ms 内存限制512MB
解析
考点:模拟
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
//区间信息
struct node{
int f,t,v;//from,to,value:开始点,结束点,值
};
vector<node> a;//存放所有的区间
int t[N];//读入的数
bool f[N];//标记删除
int n;
int main(){
scanf("%d",&n);
for(int i = 1;i <= n;i++){
scanf("%d",&t[i]);
}
//分析、存储区间
int b,e;//代表区间的开始结束
for(int i = 1;i <= n;i++){
if(i==1 || t[i]!=t[i-1]) b = i;
if(i==n || t[i]!=t[i+1]){
e = i;
//node n1;n1.f=b;n1.t=e;n1.v=t[i];a.push_back(n1);
a.push_back({b,e,t[i]});
}
}
//当还有区间存在时
while(a.size()!=0){
//获取区间的第一个数的位置,删除
for(int i = 0;i < a.size();){
printf("%d ",a[i].f);
f[a[i].f] = true;//标记删除
a[i].f++;
//判断区间的开始位置是否被删除
while(a[i].f<=a[i].t&&f[a[i].f]==true) a[i].f++;
//判断区间是否存在
if(a[i].f > a[i].t) a.erase(a.begin() + i);
else i++;
}
printf("\n");
//合并区间:如果当前区间和下一个区间的值相等
for(int i = 0;i < (int)a.size() - 1;){
if(a[i].v == a[i+1].v){
a[i+1].f = a[i].f;
a.erase(a.begin()+i);
}else i++;
}
}
return 0;
}
写法2:
队列优化模拟,时间复杂度O(n)
#include<bits/stdc++.h>
using namespace std;
int p[200001], use[200001];
struct node{
int s, e, p;
}a, b;
int main(){
int n, i, s=1, f;
queue<node>q, q2;
cin>>n;
for(i=1;i<=n;i++) cin>>p[i];
p[n+1] = 1 - p[n];
for(i=2;i<=n+1;i++)
if(p[i]!=p[i-1]){
q.push({s,i-1,p[i-1]});
s = i;
}
f = n;
while(f){
while(!q.empty()){
a = q.front();
q.pop();
while(use[a.s]&&a.s<=a.e) a.s++;
if(a.s>a.e) continue;
cout<<a.s<<' ';
f--;
use[a.s] = 1;
a.s++;
if(a.s>a.e) continue;
q2.push(a);
}
while(!q2.empty()){
a = q2.front();
q2.pop();
while(!q2.empty()){
b = q2.front();
if(b.p==a.p){
a.e = b.e;
q2.pop();
}
else break;
}
q.push(a);
}
cout<<'\n';
}
return 0;
}
写法3:
链表,时间复杂度O(n)
#include<bits/stdc++.h>
using namespace std;
int num[200010], r[200010];
struct node{
int num;//桔子苹果
int cur;//当前
int end;//结尾
int l;
int r;
}
b[200010];
int cnt=1;
int main(){
int n, i, use=0, t, nex;
cin>>n;
cin>>num[1];
b[1].cur = 1;
b[1].num = num[1];
for(i=2;i<=n;i++){
cin>>num[i];
if(num[i]!=num[i-1]){
b[cnt].r = cnt + 1;
b[cnt].end = i - 1;
cnt++;
b[cnt].l = cnt - 1;
b[cnt].cur = i;
b[cnt].num = num[i];
}
else r[i-1] = i;
}
r[0] = 1;
b[0].r = 1;
while(use<n) {
for(i=b[0].r;i!=0;i=b[i].r){
cout<<b[i].cur<<' ';
b[i].cur = r[b[i].cur];
use++;
}
t = b[0].r;
while(t!=0&&b[t].cur==0)
t = b[t].r;
b[0].r = t;
while(t!=0){
nex = b[t].r;
while(nex!=0&&b[nex].cur==0)
nex = b[nex].r;
if(nex==0){
b[t].r = 0;
break;
}
if(b[t].num==b[nex].num){
r[b[t].end] = b[nex].cur;
b[t].end = b[nex].end;
b[t].r = b[nex].r;
b[b[nex].r].l = t;
}else{
b[t].r = nex;
b[nex].l = t;
t = nex;
}
}
cout<<'\n';
}
}