1.Blah数集
题目描述
大数学家高斯小时候偶然间发现一种有趣的自然数集合Blah,对于以a为基的集合Ba定义如下:
(1)a是集合Ba的基,且a是Ba的第一个元素;
(2)如果x在集合Ba中,则2x+1和3x+1也都在集合Ba中;
(3)没有其他元素在集合Ba中了。
现在小高斯想知道如果将集合Ba中元素按照升序排列,第N个元素会是多少?
输入
输入包括一行两个数字,集合的基a(1<=a<=50))以及所求元素序号n(1<=n<=1000000)
输出
输出一个正整数,集合Ba的第n个元素值
样例输入: 样例输出:
1 100 418
题目解析:
显然该题是队列的使用。首先将a入队,然后每次将队首出队,并将队首拓展出来的数入队,直到找到第n小的元素。但是该题的拓展是2x+1及3x+1,所以有可能前一个数的3x+1比后几个数的2x+1要小,所以可能导致队列中不是从小到大,比如1、3、4,3拓展7和10,4拓展9和13,因为队列的特性,现在队列就变成了1、3、4、7、10、9、13,如果我们需要第5个数,应该是9而我们会取10,导致了错误。如果我们不断进行排序则复杂度过高;若我们依次找第几大数则不知道应该拓展到哪一步。
第一种方法可以使用STL中的优先队列。在此不再描述,因为要尝试实现队列的操作。
第二种方法即双重队列,或者说在同一个队列中,使用一个tail以及两个head,一个head只拓展2x+1,另一个只拓展3x+1,这样在保证队列递增的情况下光论某个head一定是递增的。那么每一步挑选哪一个head进行拓展,则取决于前者的2x+1和后者的3y+1哪个大,最终选择拓展小者。
唯一一个特殊情况需要考虑,即若两者相同,为了避免重复拓展,可以考虑若相同,则拓展一个后两个head都进行出队操作(+1)。
C++代码(不使用STL):
#include <iostream>
using namespace std;
#define maxn 1000000
int min(int a, int b)
{
if (a>b)
return b;
else
return a;
}
void push(int queue[maxn], int head, int &tail, int x)
{
if (head%maxn==(tail+2)%maxn)
cout<<"overflow";
else
{
tail++;
tail=tail%maxn;
queue[tail]=x;
}
}
void pop(int queue[maxn], int &head, int tail, int &y)
{
if (head%maxn==(tail+1)%maxn)
cout<<"downflow";
else
{
y=queue[head];
head++;
head=head%maxn;
}
}
int main()
{
int a,n;
cin>>a>>n;
int queue[maxn];
int number=1;
int x;
int head2=0; int head3=0; int tail=-1;
push(queue,0,tail,a);
while (number<n)
{
int y;
if (queue[head2]*2+1<queue[head3]*3+1)
{
pop(queue, head2, tail, y);
push(queue, min(head2,head3), tail, y * 2 + 1);
x=y*2+1;
}
else
if (queue[head2]*2+1>queue[head3]*3+1)
{
pop(queue, head3, tail, y);
push(queue, min(head2,head3), tail, y * 3 + 1);
x=y*3+1;
}
else
{
pop(queue, head2, tail, y);
pop(queue, head3, tail, y);
push(queue, min(head2,head3), tail, y * 3 + 1);
x=y*3+1;
}
number++;
}
cout<<x;
return 0;
}
2.海港
题目描述
小K是一个海港的海关工作人员,每天都有许多船只到达海港,船上通常有很多来自不同国家的乘客。
小K对这些到达海港的船只非常感兴趣,他按照时间记录下了到达海港的每一艘船只情况;对于第i艘到达的船,他记录了这艘船到达的时间ti (单位:秒),船上的乘客数ki,以及每名乘客的国籍 x(i,1), x(i,2),…,x(i,k);。
小K统计了n艘船的信息,希望你帮忙计算出以每一艘船到达时间为止的24小时(24小时=86400秒)内所有乘船到达的乘客来自多少个不同的国家。
形式化地讲,你需要计算n条信息。对于输出的第i条信息,你需要统计满足 ti - 86400 < tp <= ti的船只p,在所有的x(p,j)中,总共有多少个不同的数。
输入
第一行输入一个正整数n,表示小K统计了 n艘船的信息。
接下来n行,每行描述一艘船的信息:前两个整数ti和ki分别表示这艘船到达海港的时间和船上的乘客数量,接下来ki个整数x(i,j)表示船上乘客的国籍。
保证输入的ti是递增的,单位是秒;表示从小K第一次上班开始计时,这艘船在第 ti 秒到达海港。
输出
输出n行,第i行输出一个整数表示第ii艘船到达后的统计信息。
样例输入: 样例输出:
4 3
1 4 1 2 2 3 3
3 2 2 3 3
86401 2 3 4 4
86402 1 5
对于100%的测试点,1≤n≤10^5,∑ki ≤3×10^5, 1≤xi,j≤10^5,1≤ti≤10^9。
题目解析:
若使用朴素的想法,将所有读入的东西全部用二维数组保存起来再进行操作,则根据数据范围显然会爆空间。(因为每艘船人数不确定,只能每艘船都开3*10^5)但实际上总共只有3*10^5人,所以考虑以一维数组来储存人,并利用结构体或新数组来同时记录人到达的时间。于是我们就可以考虑使用队列来存储人(的国籍),对于船i,在将船中的乘客进队的同时,考虑将已经超时的(在队头)的乘客不断出队,就实现了队列中始终是当前船只所需要统计的乘客们。
至于如何找到当前船只需要统计的乘客们来自多少不同的国家,若暴力穷举,则会导致超时。所以我们考虑使用hash1数组,对于hash1[i]=k,表示当前队列中国籍为i的有k个人。对该数组的维护即为:进队时将对应的hash1[j]加一,出队时将对应的减一。若某个位置减到了0,则当前国籍数-1。若某个位置从0加到了1,则当前国籍数加一。
C++代码(不使用STL):
#include <iostream>
using namespace std;
#define maxn 1000000
struct node
{
int country;
int time;
};
void push(node queue[maxn], int head, int &tail, node x)
{
if (head%maxn==(tail+2)%maxn)
cout<<"overflow";
else
{
tail++;
tail=tail%maxn;
queue[tail]=x;
}
}
void pop(node queue[maxn], int &head, int tail, node &y)
{
if (head%maxn==(tail+1)%maxn)
cout<<"downflow";
else
{
y=queue[head];
head++;
head=head%maxn;
}
}
int main()
{
node queue[maxn];
node y;
int head=0; int tail=-1;
int n; cin>>n;
int ans=0; //ans表示有效时间范围内不同国家的数量
int hash[100100]={0};
for (int i=1; i<=n; ++i)
{
node ck; //乘客
cin>>ck.time;
int k; cin>>k;
for (int j=1; j<=k; ++j)
{
cin>>ck.country;
push(queue,head,tail,ck);
if (hash[ck.country]==0)
ans++;
hash[ck.country]++;
while (queue[head].time<=ck.time-86400)
{
pop(queue,head,tail,y);
hash[y.country]--;
if (hash[y.country]==0)
ans--;
}
}
cout<<ans<<endl;
}
return 0;
}
3.家庭问题
题目描述
有n个人,编号为1,2,……n,另外还知道存在K个关系。一个关系的表达为二元组(α,β)形式,表示α,β为同一家庭的成员。
当n,k和k个关系给出之后,求出其中共有多少个家庭、最大的家庭中有多少人?
例如:n=6,k=3,三个关系为(1,2),(1,3),(4,5)
此时,6个人组成三个家庭,即:{1,2,3}为一个家庭,{4,5}为一个家庭,{6}单独为一个家庭,第一个家庭的人数为最多。
输入
第一行为n,k二个整数(1≤n≤100)(用空格分隔)
接下来的k行,每行二个整数(用空格分隔)表示关系
输出
二个整数(分别表示家庭个数和最大家庭人数)
样例输入: 样例输出:
6 3 3 3
1 2
1 3
4 5
题目解析:
将每个人视为一个结点,则“有关系”意味着两个结点相连。则求有多少家庭就是求这个图中有多少“连通块”。而最大的家庭中的人数即为最大的连通块有多少结点。我们对于每个人,可以使用广度优先搜索,搜出这个人所在的家庭所有的成员,并且使用hash1使得搜过的人再也不会被搜,以防止家庭数多加或是造成时间冗余。每次搜完一人后,即为找到了一个新家庭(因为若之前家庭已经搜过该人,则该人不会再被作为起点广度优先搜索)。每次在搜新家庭时,都将该家庭人数纪录下来,若超过当前家庭人数的最大值,则进行最大值的替换。
最后将家庭的个数以及家庭人数的最大值输出。
C++代码(不使用STL):
#include <iostream>
#include <cstring>
using namespace std;
#define maxn 1000
void push(int queue[maxn], int &head, int &tail, int x)
{
if (head%maxn==(tail+2)%maxn)
cout<<"overflow";
else
{
tail++;
tail=tail%maxn;
queue[tail]=x;
}
}
void pop(int queue[maxn], int &head, int &tail, int &y)
{
if (head%maxn==(tail+1)%maxn)
cout<<"downflow";
else
{
y=queue[head];
head++;
head=head%maxn;
}
}
int main()
{
int queue[maxn];
int hash[110]={0};
int family[110][110]={0};
int sum=0; //sum表示家庭的个数
int n,k; int head=0; int tail=-1;
cin>>n>>k;
for (int i=1; i<=k; ++i)
{
int x,y; cin>>x>>y;
family[x][y]=1;
family[y][x]=1;
}
int ans=-1;
for (int i=1; i<=n; ++i)
if (hash[i]==0)
{
sum++;
hash[i]=sum;
head=0; tail=-1;
push(queue,head,tail,i);
int s=1;
while (head<=tail)
{
int y;
pop(queue,head,tail,y);
for (int j=1; j<=n; ++j)
if ((family[y][j]==1)&&(hash[j]==0))
{
push(queue,head,tail,j);
hash[j]=sum;
s++;
}
}
if (s>ans)
ans=s;
}
cout<<sum<<' '<<ans;
return 0;
}
浙公网安备 33010602011771号