[线段树]Fowerpot

来源:互联网 发布:手机淘宝好评率 编辑:程序博客网 时间:2024/06/04 18:47

          此题是我们学校考试的一道题,不知道我们老师是从哪里找来的。。。



         Fowerpot

【问题描述】 

Farmer John 在植物生长方面遇到了些麻烦,需要你帮助他对植物进行合理的浇水。在二维平面上

提供了 N 个雨滴的位置。 Y 代表雨滴的垂直高度, X 代表雨滴在数值轴上的位置。
每个雨滴以每秒一个单位的速度向下落(X 轴的方向)。你要把宽度为 W 的花盆放在 X 轴的某个位置
上,能够让落入花盆的第一滴雨水掉入花盆的时间和最后一滴雨水掉入花盆的时间之差最少达到 D(这
样花盆里的花朵可以得到大量的水)。滴在花盆边上的水也算作滴入花盆。有了 D 的值和 N 个雨滴的
位置,请帮助 FJ 计算出 W 可能的最小值。


【输入格式】 
第 1 行:两个整数 N 和 D;
第 2 行..1+N 行:第 i+1 行为第 i 个雨滴的坐标(X_i,Y_i);
【输出格式】 
输出共一行一个整数,即花盆宽度的最小值;如果不可能在 D 时间内装到雨水则输出"-1"。
【输入样例】 
4 5
6 3
2 4
4 10
12 15
【输出样例】 
2
【输入说明】 有 4 滴雨水,位置分别为(6,3), (2,4), (4,10) 和 (12,15)。雨水必须滴入花盆至少五
单位时间。
【输出说明】 宽度为 2 的花盆就可以实现目标。如果我们把花盆放在 X=4…6 的位置上,雨滴#1 和#3 就
能滴入,雨水的持续时间就为 10 - 3 = 7。
【数据规模】 
对于 20%的数据: 2≤N≤100; 1≤D≤100;
对于 40%的数据: 2≤N≤1,000; 1≤D≤2,000;
对于 100%的数据: 2≤N≤100,000; 1≤D≤1,000,000; 0≤X_i,Y_i≤1,000,000;


这道题,我也是做了很久,因为我还是太弱了。。。
  做这道题,我第一个反应时n^2枚举,两个两个匹配然后看是否符合题意,是否宽度更小,然后求最小宽度就行了,有些人会问:“为什么两个两个匹配就行了,可能你找的两个点之间还有其他点,怎么办?”。如果我们仔细思考一番,会发现,如果我们求的某一段区间里,除了两个端点,会有更小的值或更大的值,那么我们再进行其他点的枚举时,一定会找到这个更优的答案,所以应该能理解了吧。
n^2的话,是60分,因为数据很大,我们应该思考更优的算法。


看到区间,是不是立马能想到线段树?是的,标算就是线段树。
我们把所有点按照x轴排序一遍,然后把所有点放入tree中,先把我们的线段树存好,然后记录区间的y的最大值和y的最小值。
接着,我们来用两个变量来存储区间的头和尾。
首先,如果我们在此段区间内,最大值减最小值是大于等于D的且头小于尾,那么我们就枚举更小一点的区间,头往后移一位,并且将区间宽度更新;否则,我们就尾往后移一位。这样,就可以求出差值大于等于D的最小区间了,下面是代码。


#include<cstdio>#include<cstdlib>#include<iostream>#include<algorithm>#define maxm 1000000using namespace std;int n,d,ans=2147483647;struct Yif1{int y,x;}f[100005];struct Yif2{int maxy,miny;}tree[400020];bool cmp(Yif1 a,Yif1 b){if(a.x<b.x) return true;if(a.x>b.x) return false;if(a.y<b.y) return true;return false;}void xg(int tot,int l,int r)//区间初始化{if(l==r){tree[tot].maxy=f[l].y;tree[tot].miny=f[l].y;return;}int m=(l+r)>>1;xg(tot*2,l,m);xg(tot*2+1,m+1,r);tree[tot].maxy=max(tree[tot*2].maxy,tree[tot*2+1].maxy);tree[tot].miny=min(tree[tot*2].miny,tree[tot*2+1].miny);}int query1(int tot,int l,int r,int a,int b)//查找区间最大值{if(b<l||a>r) return -1;if(a<=l&&r<=b) return tree[tot].maxy;int q1,q2,m=(l+r)>>1;q1=query1(tot*2,l,m,a,b);q2=query1(tot*2+1,m+1,r,a,b);return max(q1,q2);}int query2(int tot,int l,int r,int a,int b)//查找区间最小值{if(b<l||a>r) return 2147483647;if(a<=l&&r<=b) return tree[tot].miny;int q1,q2,m=(l+r)>>1;q1=query2(tot*2,l,m,a,b);q2=query2(tot*2+1,m+1,r,a,b);return min(q1,q2);}int main(){freopen("fpot.in","r",stdin);freopen("fpot.out","w",stdout);scanf("%d%d",&n,&d);for(int i=1;i<=n;i++) {  scanf("%d%d",&f[i].x,&f[i].y);     }     sort(f+1,f+n+1,cmp);     xg(1,1,n);    int i=1,j=2;//i是头,j是尾,求最小区间    while(j<=n)     {     if(query1(1,1,n,i,j)-query2(1,1,n,i,j)>=d&&j>i){ ans=min(ans,f[j].x-f[i].x);i++;}     else j++; } if(ans==2147483647)  printf("-1"); else  printf("%d",ans);fclose(stdin);fclose(stdout);return 0;}
我这里有一些数据,你们要自己到我百度云里拿
http://pan.baidu.com/s/1jHDuBt0
1 0
原创粉丝点击