UVALive

来源:互联网 发布:网络主播网站排行榜 编辑:程序博客网 时间:2024/04/29 04:02

题目链接

题意:在二维坐标系第一象限中,将一块顶点在原点边长为R的正方形土地用直线x=n一分为二,左侧分给Wei,右侧分给Huo。

土地中包含N个绿洲,每个绿洲是一个矩形,其位置和大小用四元组(L,T,W,H)表示,其中(L,T)为其左上方顶点的坐标,W,H为其宽度和高度。绿洲互不重叠。

求满足以下条件的一条划分直线(直线方程 x=n,0<=n<=R,n取整数):

(1)二人各自所得土地中绿洲面积应满足Wei>=Huo 且二者之差达到最小;

(2)在满足(1)的基础上,Wei的土地面积越大越好。

数据范围:1<=R<=1000000, 1<=N<=10000, 0<=L,T <=R, 1<=W,H<=R


思路:

我真是太TMD菜了..

以下来自大佬的转载  感谢!http://www.cnblogs.com/helenawang/p/5178795.html


复杂度分析:从所给数据范围看,R在106数量级,既然n取[0,R]的整数,那么若以R为数据规模,对x=i, i:0~R进行步长为1的线性扫

描,假设每次迭代中基本操作次数为常数,则渐进复杂度为O(n)。假设计算环境1000ms的时间可完成108规模的基本运算,则本题

O(n)的线性扫描思路从渐进复杂度的意义上讲是可行的。

确定了线性扫描的思路,接下来要考虑如何把每轮迭代代价控制在常数以及扫描停止的条件。

1. 如果在每轮迭代中,都检查所有N个绿洲以求出所划分的面积,那么每轮迭代的复杂度为T(N),整体复杂度上升到了T(N*R), 即

1010显然不可行。

此方法的低效在于它没有为线性扫描这一“算法”设计合适的“数据结构”来存放绿洲的数据。题目输入的绿洲是一个个分散的个体,

而从坐标出发的线性扫描需要快速获得以扫描位置 x=i 为自变量的左侧累加面积,这一“快速”,常数最好,至少不能和N在同一数

量级;因此,要进行预处理将原始的绿洲数据转换为以横坐标为中心的统计值,以使每次迭代能用1~2个基本操作得到当前累加

面积进而判断下一步的走向。

2. 扫描可以从最左侧的x=0开始,不断向右移动(保证绿洲面积左侧 < 右侧),遇到第一个理想位置(左侧>=右侧,满足了(1))

后继续试探,直至抵达最理想的位置(左侧绿洲面积不增的条件下,为满足(2)尽量再往右移动)停止。由于扫描是线性的,可利用

一个累加变量,每次只取当前“列”的面积作累加即可。

至此,对绿洲数据的预处理结果要求已经比较明确了,即得到 x=i 代表的一段宽度为1(可以是i ~ i+1)、高度为R的土地中绿洲的

总面积,不妨用x[i]表示。

那么这段预处理所花费的时间呢,这回要以N为数据规模来考虑,假设所有绿洲被读入结构体数组中,则对j:0~N-1进行步长为1的

线性扫描,假设每次迭代中基本操作次数为常数,N在104数量级,完全可行。但处理每个绿洲真的是常数时间吗,其实应该是T(W)

,因为要把宽度切分为长度为1的W段,累加到x数组的W个元素上。除非x用的不是朴素的一维数组,否则整体的渐进复杂度应为

O(N*W),又是1010


  对于大佬上面的做法,我没存结构体,因为根据上面说的 我们只需要开数组X[I]记录以下宽度1高为h的面积即可,其余的东西记录结构体完全是在浪费空间复杂度,和我们最后研究的都没关系/

  第一次听说扫描线这个东西,我的理解就是可以通过扫描线来分割面积,将长度为W的面积分割为宽为1高为h的一块块小面积,因为我们要找的x为整数,最小为1,我们将x从0开始 往右找,并维护wei的面积,由于宽为1 那么面积就直接等于高度,当wei>=sum/2停止,然后将x继续右移(在面积不变的条件下),使得x达到最右端...很巧的做法... 想明白了很水的。。。

#include<bits/stdc++.h>#define inf 0x3f3f3f3fusing namespace std;typedef long long ll;const int maxn=1e6+10;int a[maxn];int t,n,R,x,y;ll w,h;int main(){scanf("%d",&t);while(t--){scanf("%d",&R);scanf("%d",&n);memset(a,0,sizeof(a));ll sum=0;for(int i=1;i<=n;i++){scanf("%d %d %lld %lld",&x,&y,&w,&h);sum+=w*h;for(int j=x;j<x+w;j++)a[j]+=h;//这里是+,有交叉..}ll ans=0;int k=0;for(k=0;2*ans<sum;k++){ans+=a[k];}while(a[k]==0&&k<R)k++;printf("%d\n",k);} return 0;}




二分:

二分就很好想了,只怪当时心态崩了...有点想法就直接写了...

二分的话,毫无疑问,我们肯定要二分x了,然后求左面绿洲的面积和sum/2比较,维护最优解,但是和上面的做法一样,我们找到了x之后 还要在绿洲面积不变得条件下将x可能的右移...


具体实现:

开一个结构体去保存每个绿洲的左右边界和高h,然后和每次的x比较,范围在左面的直接算整个的面积,被x的分割的计算分割的面积然后二分求最优解..

//注意这里的坐标不是按大小顺序给出的  WA大哭

#include<bits/stdc++.h>#define inf 0x3f3f3f3fusing namespace std;typedef long long ll;const int maxn=1e6+10;struct node{int l;int r;ll h;}q[maxn];int t,R,n;ll solve(int x){ll sum=0;for(int i=1;i<=n;i++){if(q[i].r<=x){sum+=(q[i].r-q[i].l)*q[i].h;}else if(q[i].l<x){sum+=(x-q[i].l)*q[i].h;}}return sum;}int main(){ ll x,y,w,h; ll ans=0;scanf("%d",&t);while(t--){ans=0;scanf("%d",&R);scanf("%d",&n);for(int i=1;i<=n;i++){scanf("%lld %lld %lld %lld",&x,&y,&w,&h);q[i].l=x;q[i].r=x+w;q[i].h=h;ans+=w*h;}int l=0,r=R+1,mid,gg;while(l<=r){mid=(l+r)/2;if(2*solve(mid)>=ans){gg=mid;r=mid-1;}elsel=mid+1;} ll tem=solve(gg);while(tem==solve(gg)&&gg<=R)gg++;printf("%d\n",gg-1);} return 0;}


0 0
原创粉丝点击