【CCF 201912-4】区块链(队列:80分)

写在前面

最近几次的CFF-CSP考试,我发现一个趋势。CSP的第3、4题会与计算机的专业知识密切相关。

例如:化学方程式 – 编译原理,RAID5 – 操作系统。而本题涉及到的是分布式系统的应用(区块链)。

题目大意

  • 给出一个网络,每个结点中都有一个主链。
  • 当一个结点的主链更新时,将会向它的邻居发送它的主链;邻居会将这个主链和邻居自己的链进行比较,决定是否接收这个链(即替换自己的链)。
  • 更新有两种情况:①结点自己产生一个块放在主链之后;②接收其它结点传过来的主链。
  • 注意:在同一时刻,若既有接收链又产生了块,那么先接收再产生块。

大致思路(见代码)

用邻接表储存网络,用FIFO队列保存发送链的信息,即三元组<接收结点的编号,接收的时刻,发送的主链>

例如,结点1和2相连,结点1在时刻b产生了一个块,结点1的主链变为(0, 1),那么将<结点2,时刻b+t ,主链(0, 1)>加入队列。

在时刻b时,只要将队列中时刻小于等于b的三元组出队,即发送给相应的结点进行更新即可。因为题目中给的b是升序排列的,因此在队列中所有三元组都是按时间顺序入队的。

80分代码(C++11)

遗憾的是,这份代码只得到了80分,猜测原因是入队次数过多,且每次入队都要复制一遍主链。

#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;

/* 发送链的信息 */
struct Message
{
    int vertex;     // 接收的结点编号
    int time;       // 预计接收的时间
    vector<int> chain;  // 主链

    Message(int a, int b, vector<int> &c) : vertex(a), time(b), chain(c) {}
};

int n, m, t, k;
vector<vector<int>> G;      // 邻接表
vector<vector<int>> chain;  // 保存各个结点的主链
queue<Message> que;         // Message队列

/* 比较主链src是否能替换掉主链dst */
bool repalce(vector<int> &dst, vector<int> &src)
{
    // 若src更长,则可以替换
    // 若src和dst一样长,且src最后一块的编号更小,也可以替换
    return src.size() > dst.size() || (src.size() == dst.size() && src.back() < dst.back());
}

int main()
{
    // 关闭同步
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    cin >> n >> m;
    G.assign(n + 1, vector<int>()); // 邻接表初始化
    chain.assign(n + 1, {0});       // 每个结点的主链初始化为{0}

    // 建立邻接表
    int x, y;
    for (int i = 0; i < m; ++i)
    {
        cin >> x >> y;
        G[x].push_back(y);
        G[y].push_back(x);
    }

    cin >> t >> k;
    cin.get();

    string line;
    int a, b, c;
    while (k--)
    {
        // 利用字符串流处理输入
        getline(cin, line);
        stringstream ss(line);
        // 若输入只有两个数,则c会保持值为-1
        c = -1;
        ss >> a >> b >> c;

        // 在时刻b时,处理所有时刻在b之前接收到的主链
        while (!que.empty() && que.front().time <= b)
        {
            int vertex = que.front().vertex;            // 结点编号
            vector<int> &new_chain = que.front().chain; // 发送出来的主链
            if (repalce(chain[vertex], new_chain))      // 如果可以更新
            {
                chain[vertex] = new_chain;              // 则更新结点vertex的主链
                for (int v : G[vertex])                 // 并且将这条主链推送给它的邻居
                    que.emplace(v, que.front().time + t, chain[vertex]);
            }
            que.pop();      // 弹出这个Message
        }

        if (c == -1)        // 如果输入的是2个数(表示一个查询)
        {
            cout << chain[a].size();
            for (int block : chain[a])
                cout << ' ' << block;
            cout << '\n';
        }
        else                // 如果输入的是3个数(表示产生一个块)
        {
            chain[a].push_back(c);  // 将块放置在主链的末尾
            for (int v : G[a])      // 并向邻居推送更新完的主链
                que.emplace(v, b + t, chain[a]);
        }
    }
}

测试用例

case 1.in

5 10
1 2
1 3
1 4
1 5
2 3
2 4
2 5
3 4
3 5
4 5
1 27
1 1 1
2 1 2
3 1 3
4 1 4
5 1 5
1 1
2 1
3 1
4 1
5 1
1 2
2 2
3 2
4 2
5 2
1 10 10
2 11 9
1 11
2 11
3 11
4 11
5 11
1 12
2 12
3 12
4 12
5 12

case 1.out

2 0 1
2 0 2
2 0 3
2 0 4
2 0 5
2 0 1
2 0 1
2 0 1
2 0 1
2 0 1
3 0 1 10
4 0 1 10 9
3 0 1 10
3 0 1 10
3 0 1 10
4 0 1 10 9
4 0 1 10 9
4 0 1 10 9
4 0 1 10 9
4 0 1 10 9

case 2.in

15 13
1 2
2 3
3 4
4 5
1 6
6 7
7 8
8 9
1 10
10 11
11 12
12 13
14 15
6 28
1 1 1
1 2 2
1 6
2 7
13 7
9 7
5 7
3 14
8 14
5 14
11 14
9 25
5 25
13 25
9 28 3
5 29 4
13 29 5
1 53
2 59 6
2 59
1 1000
3 1000
8 1000
9 1000
10 1000
13 1000
14 1000
15 1000

case 2.out

3 0 1 2
2 0 1
1 0
1 0
1 0
3 0 1 2
1 0
1 0
3 0 1 2
2 0 1
2 0 1
2 0 1
4 0 1 2 3
5 0 1 2 3 6
5 0 1 2 3 6
5 0 1 2 3 6
5 0 1 2 3 6
5 0 1 2 3 6
5 0 1 2 3 6
5 0 1 2 3 6
1 0
1 0

如果对您有帮助记得点个赞哟~

你可能感兴趣的:(CCF-CSP,往年真题,题解)