概念
使用两个指针维护一段序列区间,保证左指针一定小于右指针,并且指针只会向右移动。
右指针每次向右移动一格;对于右指针的每一个位置,左指针也向右移动,直到所代表的区间满足条件为止。
例题
洛谷P1638 逛画展
题意
博览馆正在展出由世上最佳的 $m$ 位画家所画的图画。
游客在购买门票时必须说明两个数字,$a$ 和 $b$,代表他要看展览中的第 $a$ 幅至第 $b$ 幅画(包含 $a,b$)之间的所有图画,而门票的价钱就是一张图画一元。
Sept 希望入场后可以看到所有名师的图画。当然,他想最小化购买门票的价格。
请求出他购买门票时应选择的 $a,b$,数据保证一定有解。
若存在多组解,输出 $a$ 最小的那组。
$1\leq n\le10^6$,$1 \leq a_i \leq m\le2\times10^3$。
解析
题目需要求区间的两个端点,则可用左右指针分别代表区间的两个端点。
每次左指针向右移动,直至区间里的数有 $m$ 种为止。
#include<bits/stdc++.h> using namespace std;
#define N 1000005 #define inf 2147483647
int n,m,ans=inf,sum,ansl,ansr; int a[N],f[N];
void add(int x){ f[a[x]]++; if(f[a[x]]==1) sum++; }
void mns(int x){ f[a[x]]--; if(f[a[x]]==0) sum--; }
int main(){ int i; cin>>n>>m; for(i=1;i<=n;i++) cin>>a[i]; int l=0,r=1; for(r=1;r<=n;r++){ add(r); while(sum==m&&l<=r){ mns(++l); if(ans>r-l) ans=min(ans,r-l),ansl=l,ansr=r; } } cout<<ansl<<' '<<ansr<<endl; return 0; }
|
洛谷P4653 [CEOI2017] Sure Bet
题意
现在有 $n$ 个 $A$ 类灯泡和 $n$ 个 $B$ 类灯泡,每个灯泡都有各自的权值。
我们将这些灯泡分为 $n$ 组,每组包含一个来自A类的灯泡和一个来自B类的灯泡。
你可以从中选取任意个灯泡,每选取一个灯泡需要花费 $1$ 的代价。
在你选取完之后,系统会随机在A类和B类中选择一个类型,并点亮那一类的所有灯泡。你选取的每个点亮的灯泡会给你带来等于它权值的收益。
现在请你合理选取灯泡,以最大化可能的最小收益。你只需要求出来这个收益即可。
$1.0\le A_i,B_i\le 1000.0$,$0\le n\le 10^5$。
解析
当选取的 $A$ 组物品总价值大于 $B$ 组时,只有从 $B$ 组选取才能达到更优解
从另一组选取时,需要使两组物品的价值尽可能相等
#include<bits/stdc++.h> using namespace std;
template<class T>inline void qmax(T &x,const T &y){if(x<y) x=y;}
#define N 100005
int n; double a[N],b[N],ans1,ans2,ans;
bool cmp(double a,double b){return a>b;}
int main(){ int i,l=0,r; cin>>n; for(i=1;i<=n;i++) scanf("%lf%lf",&a[i],&b[i]); sort(a+1,a+n+1,cmp),sort(b+1,b+n+1,cmp); for(r=1;r<=n;r++){ ans1+=a[r]; qmax(ans,min(ans1-(l+r),ans2-(l+r))); while(ans2<=ans1&&l<=n) ans2+=b[++l],qmax(ans,min(ans1-(l+r),ans2-(l+r))); } printf("%.4lf",ans); return 0; }
|