POJ3320 尺取法(map,set)

http://poj.org/problem?id=3320

题意:为了准备考试,Jessica开始读一本很厚的课本。要想通过考试,必须要课本上每个知识点看一遍。这本书一共有P页,第i页恰好有一个知识点ai(每个知识点都有一个编号),同一个知识点可能被多页反复提到。所以她想阅读连续的一些页把书本中所有的知识点全覆盖到。给定每页写到的知识点,求出阅读的最少页数。

因为所求区间为连续,所以我们使用尺取法。

尺取法过程:记录下每个知识点出现的次数,从区间头部把s取出时,则s对应的知识点出现的次数减1,如果此时某个知识点出现的次数为0时,则将尾部t向后推进,直到再次覆盖住所有知识点。记录每次t-s的最小值,不断更新即可。 

此算法的时间复杂度为O(NlogN)。

代码备注:

    先用set,O(n)统计出知识点的个数,再使用map存储各个知识点出现的次数。

    i,j为尺子的头尾

    mi为当前区间包含的知识点个数

    m为需要学习的知识点个数

#include<cstdio>
#include<algorithm>
#include<set>
#include<map>
using namespace std;
int a[1000010];
int min(int a,int b){
	if (a<b) return a; else return b;
}
int main()
{
	int n,m;
	scanf("%d",&n);
	set<int>s;
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		s.insert(a[i]);
	}
/*当j<n并且知识点还没有覆盖完全即(mi<m)时,
我们需要一直增加j,并且中间记录各知识点出现的次数。
执行完此次循环之后,我们一定会得到一个答案(不一定最优),所
以可以更新答案。*/
	m=s.size();//知识点个数 
	map<int,int>mp;
	int i=1,j=1,mi=0,ans=n;
	while (1){
		while (j<=n&&mi<m){
			if (mp[a[j]]==0) mi++;
			mp[a[j]]++;
			j++; 
		}
		if (mi<m) break;
/*若直至j出界,知识点个数还是不够,说明结果统计完毕,此处为循环出口*/
		ans=min(ans,j-i);
		mp[a[i]]--;
		if (mp[a[i]]==0) mi--;
		i++;
/*增加i,答案继续更新,若--mp[a[i]]==0了,
就需要再增加j以保证所有知识点的全覆盖。*/
	}
	printf("%d\n",ans);
}

声明:此文章为半原创,是在读完大佬的文章,理解领悟之后写的。

原文https://blog.csdn.net/zwj1452267376/article/details/50505890

猜你喜欢

转载自blog.csdn.net/qq_21989927/article/details/81289557