信息学奥赛一本通 ybt 1975:【16NOIP普及组】海港 | 洛谷 P2058 [NOIP2016 普及组] 海港

文章讲述了如何使用队列数据结构解决NOIP2016普及组的海港问题,涉及两种方法:一种是每个人作为队列元素,另一种是每条船作为队列元素。主要思路是利用队列管理到达时间、国籍数量的变化,确保空间效率和正确计数。
摘要由CSDN通过智能技术生成

【题目链接】

ybt 1975:【16NOIP普及组】海港
洛谷 P2058 [NOIP2016 普及组] 海港

【题目考点】

1. 模拟
2. 队列

【解题思路】

解法1:设队列,每个人是队列中的一个元素

由于人数总和(即k的加和)最大为 3 ∗ 1 0 5 3*10^5 3105,可以把每个人作为一个元素保存在队列中。
设计数数组c,c[i]表示第i国籍的人数。
设结构体Peo表示一个人,每个人包含属性:时间time、国籍nation。
t时刻有船到岸:

  • 先将队列中所有到达时间time比当前时间t早86400以上的人出队。每出队1个人,该国籍的人数减1。如果该国籍的人数变为0,则总国籍数量减1。
  • 而后把该船上所有人加入到队列中,对于每个入队的人,这个人的国籍的人数加1。如果该国籍的人数从0变为1,则总国籍数量加1。

每次有船到岸,就输出一下国籍总数。

解法2:设队列,每条船是队列中的一个元素

每条船为一个元素,包含属性vector类型的nation,保存所有人的国籍,以及time表示时间。
注意:nation必须设为长度可变的vector,这样可以节省空间。如果设为数组,则会内存超限。
t时刻有船到岸:

  • 先将队列中所有到达时间time比当前时间t早86400以上的船出队。每出队1条船,遍历该船上的所有人,每人对应的国籍人数减1。如果该国籍的人数变为0,则总国籍数量减1。
  • 而后把该船加入到队列中,对于每个船上的人,这个人的国籍的人数加1。如果该国籍的人数从0变为1,则总国籍数量加1。

每次有船到岸,就输出一下国籍总数。

【题解代码】

解法1:使用队列,每个人是队列中的一个元素
#include <bits/stdc++.h>
using namespace std;
struct Peo
{
	int time, nation;
	Peo(){}
	Peo(int a, int b):time(a), nation(b){}
};
int c[100005];//c[i]:第i国籍人数 
int main()
{
	queue<Peo> que;
	int n, t, k, x, ct = 0;//ct:总国籍数 
	cin >> n;
	for(int i = 1; i <= n; ++i)
	{
		cin >> t >> k;
		while(que.empty() == false && que.front().time <= t-86400)//只要队列不空 
		{//到港时间que.front().time比当前时间t早86400秒以上的人都出队 
			int nat = que.front().nation;
			if(--c[nat] == 0)//nat国籍的人数减1,如果该国籍人数为0 
				ct--;//总国籍数量减少 
			que.pop(); 
		}
		for(int j = 1; j <= k; ++j)
		{
			cin >> x;
			if(++c[x] == 1)//x国籍人数增加,如果从0变为1 
				ct++;//总国籍人数增加 
			que.push(Peo(t, x)); 
		}
		cout << ct << endl;
	}
	return 0;
}
解法2:设队列,每条船是队列中的一个元素
#include <bits/stdc++.h>
using namespace std;
struct Ship
{
	int time;
	vector<int> nation;
};
int c[100005];//c[i]:第i国籍人数 
int main()
{
	queue<Ship> que;
	int n, t, k, x, ct = 0;//ct:总国籍数 
	cin >> n;
	for(int i = 1; i <= n; ++i)
	{
		cin >> t >> k;
		while(que.empty() == false && que.front().time <= t-86400)//只要队列不空 
		{//到港时间que.front().time比当前时间t早86400秒以上的人都出队 
			for(int nat : que.front().nation)
			{
				if(--c[nat] == 0)//nat国籍的人数减1,如果该国籍人数为0 
					ct--;//总国籍数量减少 	
			}
			que.pop(); 
		}
		Ship ship;
		ship.time = t;
		for(int j = 1; j <= k; ++j)
		{
			cin >> x;
			ship.nation.push_back(x);
			if(++c[x] == 1)//x国籍人数增加,如果从0变为1 
				ct++;//总国籍人数增加 
		}
		que.push(ship);
		cout << ct << endl;
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值