【问题描述】
FJ的n头牛都站在二维农场上的一些坐标点(x1,y2)…(xn,yn)上,其中xi和yi都是正奇数,大小不会超过1,000,000。FJ想以建造一条很长(实际上是无限长)的南北向的栅栏的方式来分割他的农场,这个栅栏表示的直线符合直线方程(或者说一次函数)x=a(a是一个偶数,请确保FJ不会将栅栏穿过任何一头牛)。FJ同时还想建造另一条很长(实际上也是无限长)的东西向的栅栏,这个栅栏表示的直线同样满足直线方程y=b,其中b是一个偶数。这两条栅栏相交于点(a,b),并且这两条栅栏一起将他的农场分成四块。
FJ想选择a和b使得出现在这四块区域里的牛的个数是“平衡的”,即没有一块区域里有太多的牛。如果使得m是四块区域里牛的数量的最大值,那么FJ想让m最小。请帮助他决定m的最小值。
【输入格式】
第一行的输入是一个整数n,下面n行每行包含一只牛的位置,即其的x和y坐标。
【输出格式】
只有一行,即最小的m。
【输入样例】
7
7 3
5 5
7 13
3 1
11 7
5 3
9 1
【输出样例】
2
【分析】
原题数据实在太水 n^3算法乱搞搞就行了。
【数据范围】
30%的数据:1≤n≤300
50%的数据:1≤n≤1000
100%的数据:1≤n≤100000
30分算法:
void solve30()
{
int aa=maxn;
for(int i=1;i<=n;i++)
{
int xx=x[i]+1;
for(int j=1;j<=n;j++)
{
int yy=y[j]+1;
int ans1=0,ans2=0,ans3=0,ans4=0,ans;
for(int kk=1;kk<=n;kk++)
{
if(E[kk].x<xx && E[kk].y<yy) ans1++;
else if(E[kk].x<xx && E[kk].y>yy) ans2++;
else if(E[kk].x>xx && E[kk].y<yy) ans3++;
else if(E[kk].x>xx && E[kk].y>yy) ans4++;
}
ans=max(ans1,ans2);ans=max(ans3,ans);ans=max(ans4,ans);
aa=min(aa,ans);
}
}
printf("%d",aa);
}
50分优化:对于每次枚举的x,y坐标则可以使用二分猜:当上方的牛多些,则下次在下半部分猜,反之亦然,每次猜时记录最优值。O( n*n*log2 1000000) (也可以猜下标)
P.S:在猜的同时记录答案是因为猜的时候没有考虑奇偶性,难以判断,在途中猜可以避免麻烦。
bool check(int xx,int mid)
{
int yy=mid;
int ans1=0,ans2=0,ans3=0,ans4=0,ans;
for(int kk=1;kk<=n;kk++)
{
if(E[kk].x<xx && E[kk].y<=yy) ans1++;
else if(E[kk].x<xx && E[kk].y>yy) ans2++;
else if(E[kk].x>xx && E[kk].y<=yy) ans3++;
else if(E[kk].x>xx && E[kk].y>yy) ans4++;
}
ans=max(ans1,ans2);ans=max(ans3,ans);ans=max(ans4,ans);
aa=min(ans,aa);
if(ans==ans2||ans==ans4) return false;
return true;
}
void solve50()
{
int aa=maxn;
for(int i=1;i<=n;i++)
{
int xx=x[i]+1;
int A=1,B=1000000,ww;
while(A<B)
{
int mid=(A+B)/2;
if(check(xx,mid)) ww=mid,B=mid-1;
else ww=mid,A=mid+1;
}
}
printf("%d",aa);
}
100分算法:
对于x基准线的移动,我们发现每次其实只有1个点从右边移至左边,而要查询的正好是小于y基准线的元素个数,这正好符合树状数组维护动态集合的思想。
实现:开lc,rc两个树状数组,最开始元素全部在rc,x移动便将一个元素从rc删除,移至lc。
O( n*log2 1000000*log2 1000000)
int lowbit(int x){return -x&x;}
void update(int x,int *c,int flag)
{
if(flag==1)
while(x<=1000000)
c[x]++,x+=lowbit(x);
else
while(x<=1000000)
c[x]--,x+=lowbit(x);
}
int sum(int x,int *c)
{
int ret=0;
while(x>0)
ret+=c[x],x-=lowbit(x);
return ret;
}
bool check(int mid,int i)
{
int yy=mid;
int ans1=0,ans2=0,ans3=0,ans4=0,ans;
ans1=sum(yy,lc);
ans2=i-ans1;
ans3=sum(yy,rc);
ans4=n-i-ans3;
ans=max(ans1,ans2);ans=max(ans3,ans);ans=max(ans4,ans);
aa=min(aa,ans);
if(ans==ans2||ans==ans4) return false;
return true;
}
void solve100()
{
sort(E+1,E+1+n);
for(int i=1;i<=n;i++)
update(E[i].y,rc,1);
for(int i=1;i<=n;i++)
{
update(E[i].y,rc,-1);update(E[i].y,lc,1);
int A=0,B=1000000,ww;
while(A<B)
{
int mid=(A+B)/2;
if(check(mid,i)) ww=mid,B=mid-1;
else ww=mid,A=mid+1;
}
}
printf("%d",aa);
}
本文介绍了如何使用C++解决USACO竞赛中关于农场分割的平衡问题。通过建造南北和东西向的栅栏将农场分为四块,目标是找到最佳的栅栏位置以使每块区域的牛数量尽可能均衡。讨论了不同复杂度的算法,包括n^3的基础算法和利用树状数组优化到O(n * log^2 1000000)的解决方案。
1248

被折叠的 条评论
为什么被折叠?



