洛谷 P1638【逛画展】
题目描述
博览馆正在展出由世上最佳的 M 位画家所画的图画。
wangjy想到博览馆去看这几位大师的作品。
可是,那里的博览馆有一个很奇怪的规定,就是在购买门票时必须说明两个数字,a和b,代表他要看展览中的第 a 幅至第 b 幅画(包含 a 和 b)之间的所有图画,而门票的价钱就是一张图画一元。
为了看到更多名师的画,wangjy希望入场后可以看到所有名师的图画(至少各一张)。
可是他又想节省金钱。。。
作为wangjy的朋友,他请你写一个程序决定他购买门票时的 a 值和 b 值。
输入输出格式
输入格式
第一行是 N 和 M,分别代表博览馆内的图画总数及这些图画是由多少位名师的画所绘画的。
其后的一行包含 N 个数字,它们都介于 1 和 M 之间,代表该位名师的编号。
输出格式
a和 b(a<=b) 由一个空格符所隔开。
保证有解,如果多解,输出a最小的。
输入输出样例
输入样例1
12 5 2 5 3 1 3 2 4 1 1 5 4 3
输出样例1
2 7
解题思路
定义一个in数组,表示在这个区间中的每个大师画的个数,p表示种类数量。
首先循环,右端点不断向右,直到所有大师的画都在里面。
然后左端点向左移,不断踢出多余的画,直到这个区间的画是所有大师的画-1
最后看这个区间值是不是最优的,并记录最优值
直到所有区间都做完了操作,就停止,输出。
因为每个点最多进入一次区间,所以复杂度O(n)。
题解
1 #include<bits/stdc++.h> 2 using namespace std; 3 int n,m,ans=0x7fffffff,ansl,ansr; 4 int pic[1000001];//存每幅画的大师 5 int in[1000001]; 6 int read()//快读,十分节省时间 7 { 8 int x=0,f=1; 9 char ch=getchar(); 10 while(ch<'0'||ch>'9'){ 11 if(ch=='-') 12 f=-1; 13 ch=getchar(); 14 } 15 while(ch>='0'&&ch<='9'){ 16 x=(x<<1)+(x<<3)+(ch^48); 17 ch=getchar(); 18 } 19 return x*f; 20 } 21 int main() 22 { 23 cin>>n>>m; 24 for(int i=1;i<=n;i++) 25 { 26 pic[i]=read(); 27 } 28 int l=1,r=0,p=0;//左端点,右端点,区间里的种类 29 while(1) 30 { 31 bool flag1=false; 32 bool flag2=false;//初始值 33 while(p<m&&r<n)//右端点不超出并且种类还没达到 34 { 35 flag1=true;//做过操作 36 r++;//端点向右 37 if(!in[pic[r]])p++;//是新的就种类加一 38 in[pic[r]]++;//大师画的数量加一 39 } 40 while(p==m&&l<r)//保证所有的话都在里面并且左端点不超过右端点 41 { 42 flag2=true;//做过操作 43 if(in[pic[l]]==1)p--;//如果这个大师的画在区间里只有一幅,种类减一 44 in[pic[l]]--;//大师画的数量减一 45 l++;//左端点向右移 46 } 47 if(p+flag2==m&&r-l+1+flag2<ans)//因为画师的种类少了一,所以我们要加回来再记录 48 { 49 ans=r-l+1+flag2; 50 ansr=r; 51 ansl=l-flag2; 52 } 53 if(!flag1&&!flag2)break;//有一项操作没做就退出 54 } 55 56 cout<<ansl<<" "<<ansr;//输出 57 return 0; 58 }