洛谷P2058 [NOIP2016 普及组] 海港

文章介绍了两种不同的去重算法,一种是70分做法,使用暴力去重和桶排序;另一种是100分做法,利用STL数据结构优化桶排序过程,以提高效率并避免内存溢出。
摘要由CSDN通过智能技术生成

题意

1.给定n( n ≤ 1 e 5 n\leq1e5 n1e5)个数列,数列中包含:时间 t t t,个数 k ( k ≤ 3 e 5 ) k(k\leq3e5) k(k3e5) k k k个数

2.时间 t t t依次递增

3.统计从t开始,当 ( t x − t ) < 86400 (tx-t)<86400 (txt)<86400时,在 x x x个数列中不重复的 k k k个数,有多少个?
(PS:tx为第x个数列中包含的时间 t t t)

数据规模与约定

70分: 1 ≤ n ≤ 100 , ∑ k i ≤ 100 , 1 ≤ x i , j ≤ 100 , 1 ≤ t i ≤ 86400 1 \leq n \leq 100, \sum k_i \leq 100,1 \leq x_{i,j} \leq 100,1 \leq t_i \leq 86400 1n100,ki100,1xi,j100,1ti86400

100分: 1 ≤ n ≤ 1 0 5 , ∑ k i ≤ 3 × 1 0 5 , 1 ≤ x i , j ≤ 1 0 5 , 1 ≤ t i ≤ 1 0 9 1 \leq n \leq 10^5,\sum k_i \leq 3\times 10^5, 1 \leq x_{i,j} \leq 10^5,1\leq t_i \leq 10^9 1n105,ki3×105,1xi,j105,1ti109

70分做法

按照题意进行暴力去重即可

去重算法(桶排序去重)解释:

基础定义

    数列a[]:1 2 1 1 3 3 3 5(PS:qc[]数组是去重数组(桶))
		a[1]=1,a[2]=2,a[3]=1,a[4]=1,a[5]=3,a[6]=3,a[7]=3,a[8]=5.
	qc[a[i]]++;(0<i<9,i为正整数)

运行结果

	qc[1]   qc[2]   qc[3]   qc[4]   qc[5]
	  3       1       3       0       1

运行判断与最终结果

	运行完qc[a[i]]++后只需要判断qc[i]是否为0即可
		qc[1]=true,qc[2]=true,qc[3]=true,qc[4]=false,qc[5]=true;
	因为有4个为true,所以答案为4(不重复的数有4个).

70分code↓

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+50;//x[i][j]第i数列的j个数(共有k个),t[]是记录时间t的数组
int n,k[maxn]={},vis[maxn]={},qc[maxn]={},x[1001][1001];//k[]记录个数k
long long t[maxn]={};//vis[]是标记,qc[]是处理去重用的数组
int f(int o){//标记要退出的数列
	for(int i=o;i>=1;i--)
		if(t[o]-t[i]<86400) vis[i]=1;//如果时间比86400秒小,就进行标记
	return 0;
}
int qc2(int o){
	int mx=0;
	for(int i=1;i<=n;i++){
		if(vis[i]==1){//如果数列i位于86400秒内,就进行去重
			for(int j=1;j<=k[i];j++){
				qc[x[i][j]]++;//将第k个数的个数表现在qc[k]
				if(x[i][j]>mx) mx=x[i][j];//记录k个数中的最大值,方便常数查找
			}
		}
	}
	return mx;//返回最大值
}
int qc3(int r){//r代表了最右端点值,也就是k个数中的最大值
	int ans=0;//ans是答案
	for(int i=1;i<=r+1;i++){//r+1是因为要将最大值一起算进去
		if(qc[i]!=0) ans++;//不看个数,只看有没有,以此来去重
	}
	memset(qc,0,sizeof(qc));//因为已经得到答案,便要将qc[]数组清零
	return ans;//返回答案
}
int main(){
	cin>>n;//输入总数列的个数
	for(int i=1;i<=n;i++){
		cin>>t[i]>>k[i];//输入时间t和个数k
		for(int j=1;j<=k[i];j++){
			cin>>x[i][j];//输入k个数
		}
	}
	for(int i=1;i<=n;i++){
		f(i);//进行标记,看看这个数列在不在86400秒内
		cout<<qc3(qc2(i))<<endl;//输出答案
		memset(vis,0,sizeof(vis));//清零标记数组,以方便下次标记
	}
	return 0;//程序正常结束
}

但是这种做法得不到100分,因为有2个原因

1.数组开的太大而MLE

2.时间复杂度太高而TLE

代码结果

100分做法

100分做法就需要用到STL数据结构

去重算法(桶排序去重)优化:

基础定义

    数列a[]:1 2 1 1 3 3 3 5(PS:t[]数组是去重数组(桶))
		a[1]=1,a[2]=2,a[3]=1,a[4]=1,a[5]=3,a[6]=3,a[7]=3,a[8]=5.

执行顺序

	if(t[a[i]]++)是先执行了if(t[a[i]]==0) 再执行了t[a[i]]++;
		反之if(++t[a[i]]==0)是先执行了t[a[i]]++后 再执行了if(t[a[i]]==0)

优化方法

	如果if(t[a[i]]==0) 那这个数就是第一次出现,就需要记录答案
		反之,则不是第一次出现,不用记录答案
	因为t[a[i]]需要+1,所以完整语句是"if(t[a[i]++==0) ans++;"

100分code↓

#include <bits/stdc++.h>
using namespace std;
int n,t,k,k1,ans=0;//n:数列的个数,t:时间,k:k个数的个数,k1:k个数本身
const int maxn=1e5+5;//maxn是边界的最大值
struct SHIP{//定义一个船的结构题
	int time;//船进入的时间
	vector<int> people;//存储总共 k个数的容器people
};
queue<SHIP> q;//建立一个,队列q,用来存储需要的数列
int to[maxn]={0};//
int main(){
	ios::sync_with_stdio(false);//开启加速cin的优化
	cin.tie(0),cout.tie(0);//同上
	cin>>n;//输入数列的个数
	for(int i=1;i<=n;i++){
		cin>>t>>k;//输入船进入的时间,以及后面 k个数的个数
		SHIP ship;//定义一个SHIP类型的变量ship
		ship.time=t;//将这个ship进入的时间赋值为t
		for(int j=1;j<=k;j++){
			cin>>k1;//输入 k个数
			ship.people.push_back(k1);//将这k个数都压进ship中的容器people中
			if(to[k1]++==0) ans++;//桶排序去重优化找出答案
		}
		q.push(ship);//将ship这个容器整个压进队列q中
		while(!q.empty()){//当这个队列是非空的时侯运行
			SHIP x=q.front();//定义一个SHIP类型的变量x,并将队列的队头赋值进去
			if(t-x.time>=86400){//如果队头已经不在这艘船的24小时之内了
				q.pop();//将队头弹出
				int siz=x.people.size();//siz是(现有的 k个数)的总个数
				for(int j=0;j<siz;j++){//容器,队列...都是从0开始的
					if(--to[x.people[j]]==0) ans--;//进行反向去掉桶的操作
				}//反过来减去桶{队头(去过重)}的人数
			}else{
				break;//当这个队列不能减的时候,便结束这个循环
			}
		}
		cout<<ans<<endl;//输出答案
	}
	return 0;
}

完结撒花QWQ

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

花火Spark

鼓励,如星光,照亮我创作的前路

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值