CCF-CSP第41次认证第三题——进程通信

题目背景

某一天,西西艾弗岛上的居民们迎来了一个天大的好消息:他们终于有了自己的操作系统。这个操作系统有一个特性:其内部每个进程都具有动态链接接口并对外广播信息的功能。小 C 的团队负责开发该功能与内存间交互的必要环节,同时维护支持内存调度与分配的子系统。

这套系统刚开发完毕,运行稳定性未知,于是小 C 团队邀请了你来当模拟测试员。他们希望你能够帮忙模拟出这套系统的运作流程,对于各种可能情况给出其具体行为,从而帮助他们完善这套系统。由于你们的合作刚处于起步阶段,小 C 为你提供了一个简化后的模型。

初始建构:

首先认为有一段存储容量为 1010010100(保证足够大) 的初始为空的全局内存,内存地址从左到右依次编号 0,1,2⋯。为了简化模型,视这段内存的每一个字节可以存储一个对象,且我们并不关心对象具体内容。因此本题中只需用 ei∈0,1,xi∈0,1 两个状态变量代表 ii 号地址处是否被占用 以及是否有对象被存储;其中 ei=1 代表地址 i 被占用,ei=0 代表未被占用;xi=1代表有对象存储,xi=0 代表没有。初始时所有 ei,xi均为 0。

接下来考虑有 n 个准备进行对象传输的进程,依次编号 1,2,⋯,n,一开始没有任何接口与它们对接。初始状态下系统中各对象的逻辑关系可以用下图表示(例 n=2):

通信行为:

将系统内各对象的行为以操作的形式封装,接下来定义小 C 的模型中所有可能发生的操作:

new p L:建立一个只负责接收进程 p 所发出对象的新接口,同时建立在进程 p 与该接口之间进行消息传递的队列,队列存储容量为 L。

  • 设在该操作之前进程 p 共对接了 k 个接口,新建的接口会被编号为 k+1。注意,新建的接口与队列具有对应关系,且它们均独属于进程 p。

  • 接下来分配器会响应,根据最优适应原则存储队列。具体来说:

    • 分配器首先会识别内存中所有极长的未被占用的连续地址段,它们从左到右可以写成若干个非空区间的形式 [l1,r1],[l2,r2],⋯,[lt−1,rt−1],[lt,rt]。其中 rt=10100。从左到右第 ii 段的长度为 ri−li+1。
    • 接下来它会在这些段中寻找长度 ≥L且尽可能短的段(若有多个满足条件的段,则取左端点 l 最小的段)[l,r];
    • 将连续地址段 [l,l+L−1] 分配给接口 k+1,用于存储队列中的信息。这段长度为 LL 的内存空间每个位置此刻起均视为被占用,也即 ∀j∈[l,l+L−1],令 ej=1。

下例中,进程 1,2 在操作执行前均已对接各自的 11 号接口,分别占用内存区域 [2,4] 与 [9,11];进行一次 new 1 3 操作后,进程 1 对接了自身的 2 号接口,并在内存区域 [5,7]建立了对应的队列:

send p:进程 p 同时向所有其对接的接口发送一个对象。具体来说,设 p 对接了 k 个接口,则 ∀i∈[1,k],均进行如下操作:

  • 找到与 p 对接的编号为 i 的接口对应的队列,其在内存中占用的地址区间为 [a,b]。

  • 如果队列为空(即接口建立以来进程 p 从未向该接口发送过任何对象),则将对象存储在 a处,令 xa=1。

  • 否则进程 pp 向该接口发送过至少一个对象。记最近一次 发送时,对象被存储在了位置 t∈[a,b]处。若 t<b,则该次发送将对象存储在地址 t+1处,令 xt+1=1;否则 t=b,此时将对象存储在地址 a 处,令 xa=1。注意该操作可能不对某个地址的属性 x 造成改动

在上例的基础上,进行一次 send 1 操作,1 号进程会在两个队列分别占有的 4,5 号地址处各存储一个对象:

额外说明:如果在此之后连续执行 4 次 send 2 操作,则队列 2−1 会依次在地址 10,11,9,10 存储对象。

delete p i:设进程 pp 对接了 k 个接口,该操作保证 1≤i≤k。该操作使编号为 i 的接口及队列均被删除,删除时遵循如下流程:

  • 找到与进程 p 对接的编号为 i的接口对应的队列,其在内存中占用的地址区间为 [a,b][a,b]。

  • ∀i∈[a,b],将 ei​ 与 xi​ 均置为 0,视为删除所有存储在其中的对象,并将各个地址的占用状态取消

  • 将与进程 pp 所对接的其余编号大于 ii 的接口的编号均减去 1,接口与队列的对应关系不变。例如一次操作前进程 p 对接了 4 个接口,则通过操作删除 2 号接口及队列后,原先编号为 3,4 的接口与队列的编号会被依次更新为 2,3。

在上例的基础上,进行一次 delete 1 1 操作,则 1 号进程原先对应的 1 号队列及其内部的对象均会被删除:

以上便是小 C 给出的简化模型的所有基本事件定义。

题目描述

现在小 C 给出了一个长度为 qq 的操作序列,每个操作都为上述三种之一。你需要严格按照顺序模拟这些操作的运行。

小 C 为了检查模拟程序运行的过程是否正确,要求你进行如下反馈输出:

  • 每次执行完 new p L 操作,你需要输出进程 p 所对接的新接口相应队列在内存中存储的地址。设其存储在区间 [a,b],你只需输出 a。

  • 每次执行完 send p 操作,在进程 p 向其对接的 k 个接口均发送一个对象后,你需要输出该操作中 k 个新发送的对象所存储的地址的和。

为了让你们之间的合作能更进一步,请你完成小 C 的模拟任务吧!

输入格式

从标准输入读入数据。

第一行用空格隔开的两个整数 n,q,依次代表进程数量与操作数量。

接下来 q 行,每行为一个操作。操作格式如题目背景所描述。单词与数字、数字与数字之间由一个空格隔开。

输出格式

输出到标准输出。

请你对于每一个 new 操作与 send 操作,按照题目要求输出一行一个整数。

样例1输入

复制代码
2 13
new 1 2
new 1 3
send 1
delete 1 1
new 1 4
send 1
new 2 3
send 2
delete 1 2
new 1 3
send 1
delete 1 1
send 1

样例1输出

复制代码
0
2
2
5
8
9
9
5
9
6

样例1解释

读入数据自初始状态起进行完前 9次操作后变为操作解释中的状态。

每次新建操作的初始地址依次为 0,2,5,9,5;

每次发送操作所存储的所有对象所在地址和依次为 2,8,9,9,6。

样例2

见题目目录下的 2.in2.ans

样例3

见题目目录下的 3.in3.ans

子任务

保证操作均有意义。即不会建立空队列,不会在进程 x 没有对接任何接口时发送对象,不会删除不存在的队列与接口。特别注意:向某长度为 l 的队列发送超过 l 个对象的行为是有意义的。

记所有 new 操作中的参数 L 的最大值为 Lm​。

  • 对于前 40% 的测试数据,保证不存在 delete 操作;

  • 对于前 80%的测试数据,保证 Lm≤10,q≤800;

对于所有测试数据,保证 1≤n≤100,1≤q≤8000,1≤Lm≤5×105,除操作名称外所有输入数据均为非负整数。

题解

本题要求模拟一个操作系统的内存管理系统,需支持三项核心功能:进程创建、消息发送和进程终止。系统需实时维护各进程的内存分配状态及其内部消息队列情况。

核心数据结构

1. 内存空间管理(空闲内存表)

采用 vector<pair<long long, long long>> c 来管理所有空闲内存块

  • first 字段表示空闲块的起始地址
  • second 字段表示空闲块的结束地址

初始化时,整个内存空间 [0, M]M = 4×10⁹)均标记为空闲状态。

2. 进程内存分配表

通过 vector<pair<long long, long long>> a[N]N=110)为每个进程维护其已分配内存段

  • 外层下标对应进程编号(1~n)
  • 每个元素的 first 字段表示内存段起始地址
  • second 字段表示内存段长度

3. 消息队列状态表

使用 map<long long, pair<long long, long long>> b 跟踪每个内存段的消息队列状态

  • 为内存段起始地址(作为唯一标识)
  • first 字段记录当前可读取位置(队头指针)
  • second 字段记录下一个可写入位置(队尾指针)

说明:采用循环队列设计,需特别处理队尾指针回绕至队头的情况。

三大核心操作详解

操作一:NEW

功能 :为进程 x 分配长度为 y 的连续内存空间。

实现步骤(最佳适应算法)
  1. 扫描空闲内存块,筛选满足 长度 ≥ y 的可用块;
  2. 选择其中最小的可用块(优化内存利用率);
  3. 从空闲表中移除该内存块;
  4. 若分配后产生剩余空间,将剩余部分重新插入空闲表;
  5. 在进程 x 的内存段表中注册新分配段,并初始化队列指针。

返回值:成功分配的内存起始地址。


操作二:SEND

功能 :向进程 x 的所有消息队列发送消息,返回投递前的队尾指针总和。

执行流程
  1. 遍历进程 x 的所有消息队列段;
  2. 累加各队列当前队尾指针值;
  3. 执行消息入队操作:
    • 队尾指针循环后移(采用模运算实现环形队列);
    • 自动处理队列满的情况;
  4. 返回累计的原始队尾指针值。

操作三:DELETE

功能 :释放进程 x 的第 y 个内存段。

关键操作
  1. 定位待释放内存段的起止地址;
  2. 清除队列状态表中相关记录;
  3. 将释放的内存段加入空闲表;
  4. 执行内存整理:
    • 按地址排序空闲表;
    • 合并相邻或连续的内存块;
  5. 更新进程 x 的内存段列表。

代码如下

复制代码
#include<bits/stdc++.h>

using namespace std;

typedef pair<long long,long long> PII;

const long long N=110,M=4e9;
long long n,q,x,y,cnt=1;
vector<PII> a[N],c;
map<long long,PII> b;
string op;

int NEW(int x,int y){
    PII ans;
    ans.second=M;
    for(int i=0;i<cnt;i++)
        if(c[i].second-c[i].first+1>=y)
            if(ans.second-ans.first>c[i].second-c[i].first)
                ans=c[i];
    for(int i=0;i<cnt;i++)
        if(c[i].first==ans.first&&c[i].second==ans.second){
            c.erase(c.begin()+i);
            break;
        }
    c.push_back({ans.first+y,ans.second});
    a[x].push_back({ans.first,y});
    b[ans.first]={ans.first,ans.first};
    return ans.first;
}

void push(int x,int y){
    b[x].second++;
    if(b[x].second==x+y)b[x].second=x;
    if(b[x].first==b[x].second)
        b[x].first=(b[x].first+1)%(x+y)+x;    
}

int SEND(int x){
    int ans=0;
    for(auto v:a[x]){
        ans+=b[v.first].second;
        push(v.first,v.second);
    }
    return ans;
}

void DELETE(int x,int y){
    int l=a[x][y-1].first,r=l+a[x][y-1].second-1;
    b.erase(l);
    c.push_back({l,r});
    cnt++;
    sort(c.begin(),c.end());
    for(int i=0;i<c.size()-1;i++){
        if(c[i].second==c[i+1].first-1){
            c[i+1].first=c[i].first;
            c.erase(c.begin()+i);
            cnt--,i--;
        }
    }
    a[x].erase(a[x].begin()+y-1);
}

int main(){
    cin>>n>>q;
    c.push_back({0,M});
    //cout<<M;
    for(int i=1;i<=q;i++){
        cin>>op;
        if(op=="new"){
            cin>>x>>y;
            cout<<NEW(x,y)<<endl;
        }else if(op=="send"){
            cin>>x;
            cout<<SEND(x)<<endl;
        }else{
            cin>>x>>y;
            DELETE(x,y);
        }
    }
}
相关推荐
小和尚同志2 小时前
A社 npm 包事故导致 Claude Code 源码泄漏?
人工智能·aigc·claude
hz_zhangrl2 小时前
CCF-GESP 等级考试 2026年3月认证C++五级真题解析
c++·青少年编程·程序设计·gesp·c++五级·gesp2026年3月·gesp c++五级
ComputerInBook2 小时前
opencv图像处理——存储结构 Mat (Matrices)
图像处理·人工智能·opencv
Σίσυφος19002 小时前
C++ 多肽经典面试题
开发语言·c++·面试
2501_933329552 小时前
企业舆情处置技术实践:基于AI的智能监测与申诉系统架构解析
人工智能·分布式·架构·系统架构
千寻girling2 小时前
不知道 Java 全栈 + AI 编程有没有搞头 ?
前端·人工智能·后端
君科程序定做2 小时前
多源遥感与深度学习视角下耕地识别与耕地监测的局限性、研究空白与科学问题
人工智能·深度学习
七夜zippoe2 小时前
可解释AI:构建可信的机器学习系统——反事实解释与概念激活实战
人工智能·python·机器学习·可解释性·概念激活
东离与糖宝2 小时前
Java 26+Spring Boot 3.5,微服务启动从3秒压到0.8秒
java·人工智能