CSP201912-4 区块链

题目

题目可以在CSP官网中查看到哟!

结果

运行结果

算法分析

这道题目竟然也是一个模拟题,模拟的思想也不是很复杂。这道题首先要注意的就是节点更新主链的条件(有两个,题目已经很清楚地说明了),其次要注意的就是只有节点的主链更新了,其才会将自己的主链向周围节点发送,而且发送的过程是需要的时间。剩下的就是纯粹的模拟了,具体代码详解如下。

代码详解

1、全局变量

在小编的代码中含有四个 int 类型的变量,分别为:n(代表n个节点),m(代表m条边),t(代表发送所需时间),k(代表操作的数量);还含有两个 vector<vector< int >>类型的数组:graph(代表双向图),ans(存储每一个节点的主链);最后含有一个map<int, unordered_map<int, array<vector< int >, 2>>>类型的op(辅助记录某时刻、某一结点的情况)。具体如下:

int n, m, t, k;
vector<vector<int>> graph(505);
vector<vector<int>> ans(505, { 0 });
map<int, unordered_map<int, array<vector<int>, 2>>> op;
2、主函数

在主函数里面,首先要做的就是读取输入信息,根据输入信息建图,小编是利用vector<>数组建图的。接下来,读取k个操作,根据不同的操作种类进行不一样的操作:(1)操作含有两个数,这个调用query()函数查询第a个节点在时刻b时的主链;(2)操作含有三个数,这个直接按照节点数、时刻、新块信号进行更新即可。具体代码如下:

int main()
{
	freopen("in.in", "r", stdin);
	freopen("out.out", "w", stdout);
	int i, j;
	//n个节点,m条边
	cin >> n >> m;
	//建图
	for (i = 1; i <= m; i++)
	{
		int u, v;
		cin >> u >> v;
		graph[u].push_back(v);
		graph[v].push_back(u);
	}
	//发送时间t,k个操作
	cin >> t >> k;
	while (k--)
	{
		int a, b, c;
		cin >> a >> b;
		//查询
		if (cin.get() == '\n' || cin.eof())
		{
			query(b);
			cout << ans[a].size();
			for (i = 0; i < ans[a].size(); i++)
				cout << " " << ans[a][i];
			cout << endl;
		}
		//更新新块
		else
		{
			cin >> c;
			op[b][a][1].push_back(c);
		}
	}
	return 0;
}
3、查询函数query()

遍历op中的每一项元素,由于输入的特殊性,op中首个int类型的键值是按照由小到大的顺序的,如果当前元素的首个int类型的键值大于查询的时刻,则停止遍历;否则继续如下过程:遍历该时刻的每一个更新的节点,如果有接收到能使自身更新的话,则先进行自身的主链更新;接下来,如果含有新块的更新,则在主链后面直接进行更新。如上操作做完之后,如果节点有更新,则将 t 时刻后的变动更新到op中。最后将该查询时刻之前的在 op 中的项删除,具体代码如下:

void query(int time)
{
	for (auto operation_d : op)
	{
		int operation_time = operation_d.first;
		if (operation_time > time) break;
		for (auto element : operation_d.second)
		{
			int node = element.first;
			auto& in = element.second[0];
			auto& out = element.second[1];
			int flag = 0;
			if (check(ans[node], in))
			{
				flag = 1;
				ans[node] = in;
			}
			for (int b : out)
				ans[node].push_back(b);
			if (flag || !out.empty())
				update(operation_time + t, node);
		}
	}
	op.erase(op.begin(), op.upper_bound(time));
}
4、检查函数check()

该函数的目的就是判断是否达到更新条件,题目中提到的两个更新条件达到其中一个即可进行更新(也就是返回1),具体代码如下:

int check(vector<int> a, vector<int>b)
{
	if (a.size() < b.size() || (a.size() == b.size() && a.back() > b.back())) return 1;
	return 0;
}
5、更新函数update()

当有节点更新会将其更新后的主链经过时间t后发送到相邻节点中,而这个函数就是对时间t后的情况进行更新。具体代码如下:

void update(int time, int node)
{
	auto &message = ans[node];
	for (auto it : graph[node])
	{
		auto &tmp = op[time][it][0];
		if ((tmp.empty() && check(ans[it], message)) || (!tmp.empty() && check(tmp, message)))
			tmp = message;
	}
}

完整满分代码

#include<cstdio>
#include<iostream>
#include<vector>
#include<unordered_map>
#include<map>
#include<array>
using namespace std;
int n, m, t, k;
vector<vector<int>> graph(505);
vector<vector<int>> ans(505, { 0 });
map<int, unordered_map<int, array<vector<int>, 2>>> op;
int check(vector<int> a, vector<int>b)
{
	if (a.size() < b.size() || (a.size() == b.size() && a.back() > b.back())) return 1;
	return 0;
}
void update(int time, int node)
{
	auto &message = ans[node];
	for (auto it : graph[node])
	{
		auto &tmp = op[time][it][0];
		if ((tmp.empty() && check(ans[it], message)) || (!tmp.empty() && check(tmp, message)))
			tmp = message;
	}
}
void query(int time)
{
	for (auto operation_d : op)
	{
		int operation_time = operation_d.first;
		if (operation_time > time) break;
		for (auto element : operation_d.second)
		{
			int node = element.first;
			auto& in = element.second[0];
			auto& out = element.second[1];
			int flag = 0;
			if (check(ans[node], in))
			{
				flag = 1;
				ans[node] = in;
			}
			for (int b : out)
				ans[node].push_back(b);
			if (flag || !out.empty())
				update(operation_time + t, node);
		}
	}
	op.erase(op.begin(), op.upper_bound(time));
}
int main()
{
	int i, j;
	//n个节点,m条边
	cin >> n >> m;
	//建图
	for (i = 1; i <= m; i++)
	{
		int u, v;
		cin >> u >> v;
		graph[u].push_back(v);
		graph[v].push_back(u);
	}
	//发送时间t,k个操作
	cin >> t >> k;
	while (k--)
	{
		int a, b, c;
		cin >> a >> b;
		//查询
		if (cin.get() == '\n' || cin.eof())
		{
			query(b);
			cout << ans[a].size();
			for (i = 0; i < ans[a].size(); i++)
				cout << " " << ans[a][i];
			cout << endl;
		}
		//更新新块
		else
		{
			cin >> c;
			op[b][a][1].push_back(c);
		}
	}
	return 0;
}

测试用例

输入

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

输出

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值