bzoj1720: [Usaco2006 Jan]Corral the Cows 奶牛围栏

金组题什么的都要绕个弯才能AC。。不想银组套模板= =

题目大意:给n个点,求最小边长使得此正方形内的点数不少于c个

首先一看题就知道要二分边长len

本来打算用二维前缀和来判断,显然时间会爆,而且坐标最大10000是不可行的

为保证效率,检验的时间应该在O(n2

所以我们先给x排个序,以每个点的x坐标为左边界,x+len-1为右边界

然后以y为关键字从小到大序后枚举点,用双指针法O(n)更新len以内能保存多少个点

点数大于等于c就可行

 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<algorithm>
 4 using namespace std;
 5 const int maxn = 505;
 6 int x[maxn],y[maxn],l,r,a[maxn],b[maxn],c,n;
 7 
 8 bool cmp1(int a, int b){return x[a]<x[b];}
 9 bool cmp2(int a, int b){return y[a]<y[b];}
10 
11 bool check(int len){
12     int left,right,ans=0;
13     for (int i=1; i<=n; i++){
14         left=x[a[i]]; right=left+len-1;
15         ans=0;
16         int k=1;
17         for (int j=1; j<=n; j++){
18             while (y[b[k]]-y[b[j]]+1<=len && k<=n){
19                 if (x[b[k]]>=left && x[b[k]]<=right) {
20                     ans++;
21                 //    printf("  %d %d %d %d %d\n", x[b[j]], x[b[k]], y[b[j]], y[b[k]], ans);
22                 }
23                 k++;
24             }
25         //    printf("%d %d %d %d %d\n", left, right, y[b[j]], y[b[j]]+len-1, ans);
26             if (ans>=c) return 1;
27             if (x[b[j]]>=left && x[b[j]]<=right) ans--;
28         }
29     }
30     return 0;
31 }
32 
33 int main(){
34     scanf("%d%d", &c, &n);
35     for (int i=1; i<=n; i++){
36         scanf("%d%d", &x[i], &y[i]);
37         r=max(r,x[i]); r=max(r,y[i]);
38         a[i]=b[i]=i;
39     }
40     sort(a+1,a+1+n,cmp1);//x从小到大 枚举列 
41     sort(b+1,b+1+n,cmp2);//y从小到大 枚举行 
42     l=1;
43     int ans=0;
44     while (l<=r){
45         int mid=l+r>>1;
46         if (check(mid)) ans=mid,r=mid-1;
47         else l=mid+1;
48     }
49     printf("%d\n", ans);
50     return 0;
51 }

 

posted @ 2016-09-20 21:43  mzl0707  阅读(585)  评论(0编辑  收藏  举报