一些队列的题--士兵训练等
N个士兵排成一队进行军事训练,每个士兵的等级用1...K范围内的数来表示,
长官每隔1小时就随便说出M个等级a1,a2…am(1≤ai≤K,M个等级中允许有重复),
如果这M个等级组成的序列是排成一队的N个士兵等级序列的子序列,
那么训练继续;否则训练结束。长官想知道,M至少为多少时,训练才有可能结束。
例:士兵等级序列为1 5 3 2 5 1 3 4 4 2 5 1 2 3,
长官说出的等级序列为5 4,那么训练继续,
如果长官说出的等级序列为4 4 4,那么训练结束。
输入
第一行为两个整数N和K(1≤N≤100000,1≤K≤10000),
下面N行,每行一个数,按队伍顺序表示每个士兵的等级。
输出
包括一行,只包含一个数M,表示当长官至少说出M个等级的时候,训练才有可能结束。
样例输入
14 5
1
5
3
2
5
1
3
4
4
2
5
1
2
3
样例输出
3
//将原数列N个数字分成若干段(设为k'段),每段均有题意所指定的K个等级,ans=k'+1 #include<bits/stdc++.h> using namespace std; int main() { int k,n,sum,t,temp; bool num[100000]; k=0; memset(num,false,sizeof(num)); cin>>t>>n; sum=0; for(int i=1;i<=t;i++) { cin>>temp; if(num[temp]==false) //如果这个数字没有出现 { sum++; num[temp]=true; if(sum>=n) //如果达到目标了 { k++; //段数加1 sum=0; //清零工作 memset(num,false,sizeof(num)); } } } k=k+1; cout<<k<<endl; return 0; }
博览购票
博览馆正在展出由世上最佳的M位画家所画的图画。人们想到博览馆去看这几位大师的作品
。可是,那里的博览馆有一个很奇怪的规定,就是在购买门票时必须说明两个数字,a和b,
代表要看展览中的第a幅至第b幅画(包含a和b)之间的所有图画,而门票的价钱就是一张图画
一元。人们希望入场后可以看到所有名师的图画(至少各一张)。可是又想节省金钱……请你
写一个程序决定购买门票时的a值和b值。
输入
第一行是N和M,分别代表博览馆内的图画总数及这些图画是由多少位名师的画所绘画的。
其后的一行包含N个数字,它们都介于1和M之间,代表该位名师的编号。
N<=1000000,M<=2000
输出
a和b(a<=b)由一个空格符所隔开。
保证有解,如果多解,输出a最小的。
样例输入
12 5
2 5 3 1 3 2 4 1 1 5 4 3
样例输出
2 7
#include<bits/stdc++.h> using namespace std; int n,m,h,k,l,r,x,ans=100000000,cnt[2018],q[10002018]; int main() { cin>>n>>m; h=1; //开始点 for(int i=1;i<=n;i++) //枚举结束点 { scanf("%d",&x); ++cnt[x];//x这个数字出现次数加1 if(cnt[x]==1)//如果正好是0变1的时候,则数字类加1 ++k; q[i]=x;//放入队列 while(h<i&&cnt[q[h]]>1) //当头结点小于尾点,并头结点所存储的那个数字出现次数多于1 //则头结点可以右移,一直移到不能移的时候 { --cnt[q[h]]; h++; } if(k==m)//如果队列中正好出现了K种数 if(ans>i-h+1)//算出此时队列的长度,看其值是否更优 { ans=i-h+1; l=h; r=i; } } printf("%d %d\n",l,r); return 0; }
[SCOI2009]生日礼物
小西有一条很长的彩带,彩带上挂着各式各样的彩珠。已知彩珠有N个,分为K种。简单的说,可以将彩带考虑为x轴,每一个彩珠有一个对应的坐标(即位置)。某些坐标上可以没有彩珠,但多个彩珠也可以出现在同一个位置上。小布生日快到了,于是小西打算剪一段彩带送给小布。为了让礼物彩带足够漂亮,小西希望这一段彩带中能包含所有种类的彩珠。同时,为了方便,小西希望这段彩带尽可能短,你能帮助小西计算这个最短的长度么?彩带的长度即为彩带开始位置到结束位置的位置差。
Input
6 3
1 5
2 1 7
3 1 3 8
Output
3
#include<bits/stdc++.h>
#define ll long long
#define inf 999999999
using namespace std;
const int M=1e6+5;
int n,k,tot,s,t,num[65],res,ans=0x7fffffff;
struct no
{
int p,c;
}z[M];
bool cmp1(no x,no y)
{
return x.p<y.p;
}
int main()
{
scanf("%d%d",&n,&k);
for(int i=1;i<=k;i++)
{
int sum,x;
scanf("%d",&sum);
while(sum--)
{
scanf("%d",&x);
z[++tot]=(no){x,i};
}
}
sort(z+1,z+tot+1,cmp1); //按出现位置进行排序
s=1; //开始点
for(int i=1;i<=tot;i++) //枚举结束点
{
if(++num[z[i].c]==1) //如果这个点的color目前才出现一次
res++; //出现的所有color总数加1
while(res==k&&s<=tot) //如果已到达了目标
{
if(num[z[s].c]==1) //这个点的color才出现一次,则不可减
break;
num[z[s].c]--;
s++;
}
if(res==k)
ans=min(ans,z[i].p-z[s].p);
}
printf("%d\n",ans);
return 0;
}
[USACO 2013 Jan]Cow Lineup
题目描述
有N头牛(1 <= N <= 100,000) 排成一列。每头牛有一个标号,范围在0到1000000000,
第 i 头牛的编号为B(i)。多头牛可以共享一个编号。John想,如果前后相邻的多头牛
能有相同的编号,将会更好。为了使前后相连且编号相同的牛的数量尽可能多,
John将选择 K 个编号,然后移除编号与这K个编号相同的牛。请帮忙选择编号。
输入
第1行:N和K
第2至N+1行:Line i+1 第i头牛的编号 B(i)
输出
连续的编号相同的牛的最大总数。
样例输入
9 1
2
7
3
7
7
3
7
5
7
有9头牛,要选择1个编号的牛移除。
样例输出
4
提示:通过移除编号是3 的牛,将有4个编号为7的牛相连
#include <bits/stdc++.h> using namespace std; int N,K,a[100500],ans; map<int,int> t; //奶牛的编号太大了,用map进行保存 int main() { scanf("%d%d",&N,&K); for(int i=1;i<=N;++i) scanf("%d",&a[i]); int num=0,head=1; for(int i=1;i<=N;++i) { if(t[a[i]]==0) num++; t[a[i]]++; //记录a[i]这种牛出现了多少次 while(num>K+1) //队内中只保存K+1个种类的牛 { t[a[head]]--; if(t[a[head]]==0) num--; head++; } ans=max(t[a[i]],ans); } printf("%d\n",ans); return 0; }
著名旅游城市 B 市为了鼓励大家采用公共交通方式出行,推出了一种地铁换乘公交
车的优惠方案:
1. 在搭乘一次地铁后可以获得一张优惠票,有效期为 45 分钟,在有效期内可以
消耗这张优惠票,免费搭乘一次票价不超过地铁票价的公交车。 在有效期内指
开始乘公交车的时间与开始乘地铁的时间之差小于等于 45 分钟,即:
tbus - tsubway <= 45
2. 搭乘地铁获得的优惠票可以累积,即可以连续搭乘若干次地铁后再连续使用优
惠票搭乘公交车。
3. 搭乘公交车时,如果可以使用优惠票一定会使用优惠票;如果有多张优惠票满
足条件,则优先消耗获得最早的优惠票。
现在你得到了小轩最近的公共交通出行记录,你能帮他算算他的花费吗?
【输入格式】
输入文件的第一行包含一个正整数 n,代表乘车记录的数量。
接下来的 n 行,每行包含 3 个整数, 相邻两数之间以一个空格分隔。第 i 行的
第 1 个整数代表第 i 条记录乘坐的交通工具, 0 代表地铁, 1 代表公交车;第 2 个
整数代表第 i 条记录乘车的票价 pricei ;第三个整数代表第 i 条记录开始乘车的时
间 ti (距 0 时刻的分钟数)。
我们保证出行记录是按照开始乘车的时间顺序给出的,
且不会有两次乘车记录出现在同一分钟。
【输出格式】
输出文件有一行, 包含一个正整数ans, 代表小轩出行的总花费
input
6 0
10 3
1 5 46
0 12 50
1 3 96
0 5 110
1 6 135
output
36
【输入输出样例 1 说明】
第一条记录,在第 3 分钟花费 10 元乘坐地铁。
第二条记录,在第 46 分钟乘坐公交车,可以使用第一条记录中乘坐地铁获得的优
惠票,因此没有花费。
第三条记录,在第 50 分种花费 12 元乘坐地铁。
第四条记录,在第 96 分钟乘坐公交车,由于距离第三条记录中乘坐地铁已超过 45
分钟,所以优惠票已失效,花费 3 元乘坐公交车。
第五条记录,在第 110 分钟花费 5 元乘坐地铁。
第六条记录,在第 135 分钟乘坐公交车,由于此时手中只有第五条记录中乘坐地铁
获得的优惠票有效,而本次公交车的票价为 6 元,高于第五条记录中地铁的票价 5 元,
所以不能使用优惠票,花费 6 元乘坐公交车。
总共花费 36 元
对于 100% 的数据, n <= 10^5, ti <= 10^9, 1 <= pricei <= 1,000。
noip2019普及组 公交换乘
这道题目有个限制很重要:
不会有两次乘车记录出现在同一分钟
这就说明对于每个公交车来说,所能使用的优惠券不会超过45张。即我们可以完美避开各种复杂的数据结构优化了。
因此我们可以从前往后扫描每条记录,同时用一个队列维护当前车次可以使用的优惠券区间:
如果当前记录是火车,则加入维护的优惠券区间;
如果当前记录是公交车,则线性扫描一遍队列中所有优惠券,找到第一个未被使用过的且大于等于当前价格的优惠券即可;
可以用一个bool数组对优惠券判重,以保证每张优惠券最多只被用一次。
时间复杂度
最坏情况下,对于每条公交车记录都需要遍历45张优惠券,因此时间复杂度是 O ( 45 n )
#include<cstdio> #include<iostream> #include<cmath> #include<cstring> #include<string> #include<algorithm> using namespace std; const int MAX=2147483647; const int N=1e6; struct node { int by,t,p; } a[N]; int n,head=1,tail,ans,que[2*N][3]; void input() { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d%d%d",&a[i].by,&a[i].p,&a[i].t); //出行方式,票价,时间 } int main() { //fre(); input(); for(int i=1;i<=n;i++) { if(!a[i].by) //地铁 { que[++tail][0]=a[i].t+45; //优惠券有效期 que[tail][1]=a[i].p; //优惠券票价 ans+=a[i].p; //坐地铁买票 } else //公交 { bool flag=0; for(int j=head;j<=tail;j++) { if(a[i].t>que[j][0]) head++; //过期作废 else if(a[i].p<=que[j][1]&&!que[j][2]) //票价要不大于优惠券金额且没用过 { que[j][2]=1; flag=1; break; } } if(!flag) ans+=a[i].p; //不能免票,买票 ! } } printf("%d",ans); return 0; }