zoukankan      html  css  js  c++  java
  • 花盆

    题目传送门

    单调队列的题目还是滑动窗口最典型,如果猜到可以用单调队列解题,看看能否通过什么方法转化成滑动窗口(如二分答案)。单调队列的题一般涉及最大值和最小值的问题,好像有的题目也可以用ST表解决。
    这道题因为知道是单调队列才做的(硬上单调队列...)
    简化题意:找到(x)坐标相差最近的两个点,使它们的纵坐标差值大于等于(D)
    (先将点按照(x)由小到大排序)
    最开始两个思路:

    思路一:枚举每个点,把它当做低的那个点(这样答案一定会被枚举到)。这就要找它左边比它高的点(右边的情况倒着再来一遍)。维护一个单调递减的序列,因为(x)使有序的,所以单调队列中越靠左的点里当前点越远,并且越高。因此二分出(或者直接枚举)一个符合条件的(y),用它和当前点的距离更新答案。
    思路二:二分一个答案(L),还是枚举每个点,把它当做低的那个点(这样答案一定会被枚举到)。这就要找它左边比它高的点(右边的情况倒着再来一遍)。维护一个单调递减的序列,因为(x)使有序的,所以单调队列中越靠左的点里当前点越远,并且越高。所以二分出(x)据当前点小于等于(L)的单调队列中最左边的点,只要它们两个的纵坐标的差值大于等于(D),就(return) (true)。如果一直没有(return),那么就不可行。

    只写了思路二的代码:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define LL long long
    #define mid ((l+r)>>1)
    #define midd ((ll+rr)>>1)
    using namespace std;
    const LL N = 100005;
    LL n,D,maxx,minn=1000006,l=1,r;
    LL maxxl,minnl=1000006,ans;
    struct node{
    	LL x,y;
    	bool friend operator < (const node& a,const node& b)
    	{ return a.x<b.x; }
    }a[N],q[N];
    LL aaa[40000000];
    inline int read()
    {
    	int ans=0,w=1;
    	char c=getchar();
    	while((c<'0'||c>'9')&&c!='-') c=getchar();
    	if(c=='-') { w=-1; c=getchar(); }
    	while(c>='0'&&c<='9')
    	{ ans=ans*10+c-'0'; c=getchar(); }
    	return ans*w;
    }
    bool check(LL u)
    {
    	l=1,r=0;
    	for(LL i=1;i<=n;i++)
    	{
    		while(q[r].y<=a[i].y&&r) --r;
    		LL ll=1,rr=r,ansp=-1;
    		while(ll<=rr)
    		{
    			if(a[i].x-q[midd].x<=u) ansp=midd,rr=midd-1;
    			else ll=midd+1;
    		}
    		if(q[ansp].y-a[i].y>=D&&ansp>0) return true;
    		q[++r].x=a[i].x;
    		q[r].y=a[i].y;
    	}
    	l=1;r=0;
    	for(LL i=n;i>=1;i--)
    	{
    		while(q[r].y<=a[i].y&&r) --r;
    		LL ll=1,rr=r,ansp=-1;
    		while(ll<=rr)
    		{
    			if(q[midd].x-a[i].x<=u) ansp=midd,ll=midd+1;
    			else rr=midd-1;
    		}
    		if(q[ansp].y-a[i].y>=D&&ansp>0) return true;
    		q[++r].x=a[i].x;
    		q[r].y=a[i].y;
    	}
    	return false;
    }
    int main()
    {
    //	freopen("a.in","r",stdin);
    //	freopen("a.out","w",stdout);
    //	cin>>n>>D;
    //	scanf("%lld%lld",&n,&D);
    	n=read(); D=read();
    	for(LL i=1;i<=n;i++)
    	{
    		scanf("%lld%lld",&a[i].x,&a[i].y);
    		minn=min(minn,a[i].y);
    		maxx=max(maxx,a[i].y); 
    		minnl=min(minnl,a[i].x);
    		maxxl=max(maxxl,a[i].x); 
    	}
    //	cout<<maxx<<"*"<<minn<<endl;
    	if(maxx-minn<D) { printf("-1
    "); return 0; }
    	sort(a+1,a+n+1);
    	LL l=1,r=maxxl-minnl;
    	ans=r;
    	while(l<=r)
    	{
    		if(check(mid)) ans=mid,r=mid-1;
    		else l=mid+1;
    	}
    	printf("%lld
    ",ans);
    	return 0;
    }
    

    写完另一个算法,又回来改了半天,还是没改对,虽然感觉是和另一个算法一样的错误。

    题解思路:二分出一个(L),求从左到右每一个长度为(L)的区间中的最大值和最小值(滑动窗口),看是否有满足条件的。

    第一次交的时候90分:
    enter image description here
    100分:
    enter image description here

    AC代码:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define mid ((l+r)>>1) 
    using namespace std;
    const int N = 100005;
    const int inf = 2147483647;
    int n,D,l1=1,r1,l2=1,r2;
    int ans,min1=inf,min2=inf,max1,max2;
    struct node{
    	int x,y;
    	// bool friend operator <(const node& a,const node& b)
    	// { return a.x<b.x; }
    }a[N],q1[N],q2[N];
    bool cmp(node a,node b)
    { return a.x<b.x; }
    bool check(int u)
    {
    	l1=l2=1; r1=r2=0; 
    	for(int i=1;i<=n;i++)
    	{
    		while(l1<=r1&&a[i].x-q1[l1].x>u) ++l1;
    		while(l2<=r2&&a[i].x-q2[l2].x>u) ++l2;
    		while(q1[r1].y<=a[i].y&&r1) --r1;
    		while(q2[r2].y>=a[i].y&&r2) --r2;
    		q1[++r1].x=a[i].x; q1[r1].y=a[i].y;
    		q2[++r2].x=a[i].x; q2[r2].y=a[i].y;
    		if(q1[l1].y-q2[l2].y>=D) return true;
    	}
    	return false;
    }
    int main()
    {
    	scanf("%d%d",&n,&D);
    	for(int i=1;i<=n;i++)
    	{
    		scanf("%d%d",&a[i].x,&a[i].y);
    		min1=min(min1,a[i].x);
    		min2=min(min2,a[i].y);
    		max1=max(max1,a[i].x);
    		max2=max(max2,a[i].y);
    	}
    	if(max1-min1<D) { printf("-1
    "); return 0 ;}
    	sort(a+1,a+n+1,cmp);
    	int l=1,r=max1-min1;
    	while(l<=r)
    	{
    		if(check(mid)) ans=mid,r=mid-1;
    		else l=mid+1;
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    Linux面试题大全
    数据库学习002
    数据学习001
    003
    002
    001
    金蝶清空日志数据库脚本
    表格批量导入金蝶专业版销售订单
    金蝶单据清空记账标志
    金蝶单据字段审核后可修改
  • 原文地址:https://www.cnblogs.com/karryW/p/11374030.html
Copyright © 2011-2022 走看看