2021年CSP-J复赛真题解析

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

解析

考点:数论,枚举优化

题意: [l,r] % n 的最大数。
本题暴力求解能拿 90 分,时间复杂度O(l−r)

参考代码:

#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;
}

正确的做法

本题分为两种情况:
[l,r] % n ,这个范围的结果,不存在 n-1 这个值,说明结果是递增的,答案应该是 r%n
否则,答案是 n-1
如何判断是否存在其余的结果是 n-1
判断值: l % n + (r - l) < n – 1 ,说明取不到 n-1

参考代码

#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

  1. 检查字符串是否是正常网络字符串,可以用stringstream。
  2. 用map存储服务端,客户端检测是否匹配。
  3. 也可以用sscanf来匹配网络字符串的个数,参考checknet2函数。
  4. 参考代码
    
    #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

解析

考点:模拟

题意分析:将连续相等的元素的第 1 个数的位置输出,删除该元素,由于数据量比较
大,不适合直接模拟。
将每个连续区间的 [ 开始位置 , 结束位置 , ] 获取出来,这样每个区间的第 1 个数的位置,
其实就是每个区间的开始位置,输出开始位置后,开始位置 ++ ,要注意分析位置 ++ 后对应
的位置是否被删除了(合并导致的中间部分元素被删除),以及分析位置 ++ 后这个区间是否
还存在。
也要注意分析,如果某区间删除后,是否会导致前后的区间需要合并。
参考代码:
#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';
    }
}

  • 25
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值