Google中国2014校园招聘笔试Round B China New Grad Test Problem B. Meet and party

来源:互联网 发布:中国联通沪港通数据 编辑:程序博客网 时间:2024/05/21 19:48

Problem

Little Sin lives in a Manhattan-grid city, a 2D plane where people can only go north, west, south or east along the grid. The distance from (x1, y1) to (x2, y2) is |x1 - x2| + |y1 - y2|.

Little Sin really likes to party and is hoping to host a house party in Manhattan this Sunday. Little Sin has collected a list of people who will attend, and now needs to decide at whose home she will host the party.

Little Sin invited all of the people in several rectangular areas, and all of those people have said yes. A rectangular area is denoted as (x1, y1, x2, y2), where x1 ≤ x2, y1 ≤ y2. People who live in a rectangular area fill all integral points inside it. So there are a total of (x2 - x1 + 1) * (y2 - y1 + 1) people in the rectangular area (x1, y1, x2, y2).

Little Sin knows the coordinates of those rectangular areas. She wants the party to be hosted at the home of one of the people who is attending, but she also doesn't want everyone else to have to travel very far: she wants to minimize the sum of all distances from all attendees' houses to the party. Can you help her?

Input

The first line of the input gives the number of test cases, T. T test cases follow. Each test case starts with a line containing a single integer: the number of rectangular areas, B. Blines follow. Each line contains 4 integers: x1, y1, x2, y2, denoting the coordinates of a rectangular area of people Little Sin has invited to her party.

Output

For each test case, output one line containing "Case #t: x y d", where t is the case number (starting from 1) and (x, y) is the coordinates of the person whose home the party should be hosted. If there are multiple positions with the same minimum total distance, choose the one with the smallest x. If there are still multiple positions, choose the one with the smallest y. The value d is the sum of the distances from all attendees' houses to the point (x, y).

Limits

1 ≤ T ≤ 10.
|x1|, |y1|, |x2|, |y2| ≤ 109.
x1 ≤ x2, y1 ≤ y2.
The rectangular areas within a test case don't intersect.

Small dataset

1 ≤ B ≤ 100.
1 ≤ Total number of people in each test case ≤ 1000.

Large dataset

1 ≤ B ≤ 1000.
1 ≤ Total number of people in each test case ≤ 1000000.

Sample

 


Input 
 
Output 
 
210 0 2 23-1 2 -1 20 0 0 01 3 1 3

 

类型:其他  难度:2.5

题意:B个矩形在图中,互不相交,设左下角点(x1,y1),右上角点(x2,y2),那么矩形中共有点(x2-x1+1)*(y2-y1+1),点与点的距离记为:|x2-x1|+|y2-y1|,在所有矩形中找一个点,该点到图中所有矩形中的点的距离和最小。若多个点距离最小,取x最小的,若x相同,取y最小的。

分析:一看就有思路的题,但还是做了很久。。优化的过程如下,三种解法,如只看最优解跳到(3)即可:(设总点数为N)

(1)最简单的方法,就是遍历每个点,计算所有点到该点的距离和,取距离最小的点,复杂度O(N^2),小数据时N=1000,可以用这个方法解出。代码如下:

#include<cstdio>#include<cstring>#include<cmath>using namespace std;const int MAX=1010;int b;int re[MAX][4];long long caldis(int x,int y){long long dis=0;for(int i=0; i<b; i++){int x1 = re[i][0];int y1 = re[i][1];int x2 = re[i][2];int y2 = re[i][3];for(int j=x1; j<=x2; j++)for(int k=y1; k<=y2; k++){dis += abs(x-j)+abs(y-k);}}return dis;}int main(){freopen("B-small-practice.in","r",stdin);freopen("B-small-practice.out","w",stdout);int t;scanf("%d",&t);for(int cnt=1; cnt<=t; cnt++){scanf("%d",&b);int num=0;for(int i=0; i<b; i++) {for(int j=0; j<4; j++){scanf("%d",&re[i][j]);}num += (re[i][2]-re[i][0]+1)*(re[i][3]-re[i][1]+1);}int x=-1,y=-1;long long d=2000000000;for(int i=0; i<b; i++){int x1 = re[i][0];int y1 = re[i][1];int x2 = re[i][2];int y2 = re[i][3];long long tmpd=0;for(int j=x1; j<=x2; j++)for(int k=y1; k<=y2; k++){tmpd = caldis(j,k);//printf("%d %d %d %lld\n",i,j,k,tmpd);if(tmpd < d || (tmpd==d && (j<x || (j==x && k<y)))){x = j;y = k;d = tmpd;}}} printf("Case #%d: %d %d %lld\n",cnt,x,y,d);}} 


(2)大数据时N=10^6,O(N^2)显然不可行,一开始考虑,计算某个点到一个矩形中所有点的距离时,可以在O(1)内算出,那么总时间优化为O(NB),虽然是N*B=10^9,时间稍长(1分钟),但是勉强得出了结果。设某点坐标(x,y),矩形为(x1,y1,x2,y2),那么点到矩形的距离和dis计算为:

x1c = x-x1;y1c = y-y1;x2c = x2-x;y2c = y2-y;sx1 = (x1c>=0)?1:-1;sx2 = (x2c>=0)?1:-1;sy1 = (y1c>=0)?1:-1;sy2 = (y2c>=0)?1:-1;dis += (y2-y1+1)*(sx1*(1+x1c)*x1c + sx2*(1+x2c)*x2c)/2 + (x2-x1+1)*(sy1*(1+y1c)*y1c + sy2*(1+y2c)*y2c)/2;


该式的意义:先计算矩形上的点移动到横坐标为x的线上所走的距离和,在计算移动到纵坐标为y的线走的距离和,相加即为所求。

完整代码为:

#include<cstdio>#include<cstring>#include<cmath>#include<cstdlib>using namespace std;const int MAX=1100;int b;int re[MAX][4];long long caldis(int tm,int tx,int ty){long long dis=0;for(int i=0; i<b; i++){long long x1,y1,x2,y2,x1c,y1c,x2c,y2c,sx1,sx2,sy1,sy2;x1 = re[i][0];y1 = re[i][1];x2 = re[i][2];y2 = re[i][3];x1c = tx-x1;y1c = ty-y1;x2c = x2-tx;y2c = y2-ty;sx1 = (x1c>=0)?1:-1;sx2 = (x2c>=0)?1:-1;sy1 = (y1c>=0)?1:-1;sy2 = (y2c>=0)?1:-1;dis += (y2-y1+1)*(sx1*(1+x1c)*x1c + sx2*(1+x2c)*x2c)/2 + (x2-x1+1)*(sy1*(1+y1c)*y1c + sy2*(1+y2c)*y2c)/2;}return dis;}int main(){freopen("B-large-practice.in","r",stdin);freopen("B-large-practice.out","w",stdout);int t;scanf("%d",&t);for(int cnt=1; cnt<=t; cnt++){scanf("%d",&b);//printf("b[%d]:%d\n",cnt,b);for(int i=0; i<b; i++) {for(int j=0; j<4; j++){scanf("%d",&re[i][j]);}}int x,y;long long d = -1;for(int i=0; i<b; i++){int x1 = re[i][0];int y1 = re[i][1];int x2 = re[i][2];int y2 = re[i][3];long long tmpd;//printf("%d %d %d\n",i,x2-x1,y2-y1);for(int j=x1; j<=x2; j++)for(int k=y1; k<=y2; k++){tmpd = caldis(i,j,k);//printf("%d %d %d %d %lld\n",cnt,i,j,k,tmpd);if(d<0 || tmpd < d || (tmpd==d && (j<x || (j==x && k<y)))){x = j;y = k;d = tmpd;}}} printf("Case #%d: %d %d %lld\n",cnt,x,y,d);}} 

(3)根据(2)的思路继续优化,既然能在O(1)求出点到一个矩形的距离,能否通过预先计算一些数据,在O(1)内算出点到所有矩形的距离?这样的话,整体时间复杂度为O(N),在可接受范围。

方法如下,参考了http://blog.csdn.net/albani/article/details/12685639:

对于每个x轴坐标,计算参数组dx[k]=(x,p,sump,sumd),x为当前x轴左边,p为横坐标为x的线上有多少人

统计好所有x坐标的x和p后,按x从小到大排序,设dx数组下标范围为[1,cx],sump即1到k的总人数,sumd即1到k的总距离,即:

dx[k].sump = dx[1].p + dx[2].p + ... + dx[k].p,

dx[k].sumd = dx[1].p*dx[1].n + dx[2].p*dx[2].n + ... + dx[k].p*dx[k].n

和(2)类似,给出点(x,y),可以通过上述数据在O(1)时间计算出(x,y)到所有点的距离,x轴和y轴分别计算。

所有点到x的距离为:

dis = (x*dx[k-1].sump - dx[k-1].sumd) + (dx[cx].sumd-dx[k].sumd - x*(dx[cx].sump-dx[k].sump));

两个加项分别为,横坐标小于x的点到x这条线的距离的和,横坐标大于x的点到x这条线的和。

y轴与x轴类似。

 

注意两个细节,被卡了很久:

1、由于坐标范围为1-10^9,点数N=10^6,所以总距离超过了int的范围,要用long long

2、结构体中n,p用的是int,但是要用n*p初始化sumd,后者的类型是long long,此时必须加long long强制转换,否则会出错,原因是n*p可能会超过int的范围

 

代码如下:

#include<cstdio>#include<cstring>#include<cmath>#include<cstdlib>#include<map>using namespace std;const int MAXR=1100;const int MAXN=1001000;int b;int re[MAXR][4];typedef struct{int n,p;long long sump,sumd;}node;node dx[MAXN],dy[MAXN];int cx,cy;map<int,int> inx,iny;int cmp(const void *a, const void *b){return ((node*)a)->n - ((node*)b)->n;}long long caldis(int x,int y){long long dis=0;int ix = inx[x], iy = iny[y];dis += (x*dx[ix-1].sump - dx[ix-1].sumd) + (dx[cx].sumd-dx[ix].sumd - x*(dx[cx].sump-dx[ix].sump));dis += (y*dy[iy-1].sump - dy[iy-1].sumd) + (dy[cy].sumd-dy[iy].sumd - y*(dy[cy].sump-dy[iy].sump));return dis;}int main(){freopen("B-large-practice.in","r",stdin);freopen("B-large-practice.out","w",stdout);int t;scanf("%d",&t);for(int cnt=1; cnt<=t; cnt++){scanf("%d",&b);inx.clear();iny.clear();memset(dx,0,sizeof(dx));memset(dy,0,sizeof(dy));cx = cy = 0;for(int i=0; i<b; i++) {for(int j=0; j<4; j++)scanf("%d",&re[i][j]);int x1 = re[i][0];int y1 = re[i][1];int x2 = re[i][2];int y2 = re[i][3];for(int j=x1; j<=x2; j++){int tmp = inx[j];if(tmp==0) tmp = ++cx;dx[tmp].n = j;dx[tmp].p += y2-y1+1;inx[j] = tmp;}for(int k=y1; k<=y2; k++){int tmp = iny[k];if(tmp==0) tmp = ++cy;dy[tmp].n = k;dy[tmp].p += x2-x1+1;iny[k] = tmp;}}qsort(dx+1,cx,sizeof(node),cmp);qsort(dy+1,cy,sizeof(node),cmp); for(int i=1; i<=cx; i++){dx[i].sump = (long long)dx[i].p;dx[i].sumd = (long long)dx[i].n * dx[i].p;inx[dx[i].n] = i;//printf("x %d %d %lld %lld\n",dx[i].n,dx[i].p,dx[i].sump,dx[i].sumd);}for(int i=1; i<=cy; i++){dy[i].sump = (long long)dy[i].p;dy[i].sumd = (long long)dy[i].n * dy[i].p;iny[dy[i].n] = i;//printf("y %d %d %lld %lld\n",dy[i].n,dy[i].p,dy[i].sump,dy[i].sumd);}for(int i=1; i<=cx; i++){dx[i].sump += dx[i-1].sump;dx[i].sumd += dx[i-1].sumd;//printf("%d %d %d %lld %lld\n",i,dx[i].n,dx[i].p,dx[i].sump,dx[i].sumd);}for(int i=1; i<=cy; i++){dy[i].sump += dy[i-1].sump;dy[i].sumd += dy[i-1].sumd;}int x=-1,y=-1;long long d=-1;for(int i=0; i<b; i++){int x1 = re[i][0];int y1 = re[i][1];int x2 = re[i][2];int y2 = re[i][3];long long tmpd=0;for(int j=x1; j<=x2; j++)for(int k=y1; k<=y2; k++){tmpd = caldis(j,k);//printf("%d %d %d %lld\n",i,j,k,tmpd);if(d<0 || tmpd < d || (tmpd==d && (j<x || (j==x && k<y)))){x = j;y = k;d = tmpd;}}}printf("Case #%d: %d %d %lld\n",cnt,x,y,d);}}