COCI 2011-2012 Contest #7] 送票 (SETNJA) 题目描述 Mirko要给朋友们送音乐会门票。朋友的家可表示为二维平面的网格。Mirko在行走时,可以朝8个方向移动,每次都是整数坐标。他每一步朝上、下、左、右以及4个对角方向走一格。 每个朋友的家可表示为平面上的点(x,y)。当Mirko到某朋友家送票时,朋友也可以走出来迎接他,也可以向8个方向行走。因此,Mirko和这个朋友最多可以在距离他家P步远的位置相遇。P随每个朋友不同。 Mirko的起始位置与终点位置都未知。 题目要求求出:Mirko送完所有票的最少移动步数。 输入 第1行:Mirko的朋友数N (2 ≤ N ≤ 200 000) 接下来N行,每行3个数,表示: x, y, and P (0 ≤ x, y, P ≤ 200 000). 朋友按Mirko送票的顺序给出 输出 第1行:1个整数,表示Mirko必须走的最少移动步数 样例数据 样例输入 样例输出 3 3 10 2 8 4 2 2 5 2 4 解析 每一个Mirko的朋友都是由X,Y,P描述的。注意到,与一个朋友的可能的会合点形成了一个以(X,Y)为中心的正方形,更确切地说,是一个相对顶点在(X-P,Y-P)和(X+P,Y+P)的正方形。 那么,现在这个问题就被简化为寻找按顺序经过所有正方形的最短可能路线。 对于K (1<=K<=N),我们考虑按顺序访问正方形1~K的所有可能的最短路径。设S(K)为这些路径的最终节点的集合,即我们以最佳策略访问前K个朋友后可以结束的所有位置。我们再设d(K)为这些最短路径的长度。 注意到,S(1)的点所组成的形状恰好是第一个朋友所对应的正方形,因为我们从这个正方形的任何点都可以开始(并结束)对这个朋友的访问,且当前的路径长度为0。 我们的程序要继续找到S(2)、S(3)......S(N)。我们会发现,所有的集合都是矩形。那么现在,我们就要看看怎样由S(K)找到S(K+1)。 令D表示S(K)中的任意一点到S(K+1)(即我们下一个要访问的人)的最短距离。那么d(K+1)=d(K)+D。 如果我们在四个方向上对矩形S(K)扩展D个单位,我们得到最佳访问前K个朋友再向任意方向移动D个单位后可以到达的所有点。因此,扩展的矩形与第(K+1)个朋友所对应的正方形的交集就是我们移动d(K)+D(根据D的定义,这个交集一定不是空集)个单位距离后能与第(K+1)个朋友会面的点。那么,这个交集就恰好是S(K+1)。由于这个交集是由一个矩形和一个正方形得到的(正方形和矩形的交点都与相同的轴对其),这个交集就一定是一个矩形。(还是搞不懂?画画图吧) 最终的解就只是在寻找S(2),S(3),......,S(N)时获得的所有D值的和,这恰好就是d(N)。
代码:
#include<cstdio> #include<algorithm> #include<iostream> using namespace std; struct rectangle { int x1,x2,y1,y2; rectangle(){} rectangle(int x,int y,int p) { x1=x-p; x2=x+p; y1=y-p; y2=y+p; } }; int main() { int n; scanf("%d",&n); int x,y,p; scanf("%d%d%d",&x,&y,&p); rectangle S(x,y,p); long long ans=0; for(int i=1; i<n; ++i) { scanf("%d%d%d",&x,&y,&p); rectangle A(x,y,p ); int D=max(max(A.x1-S.x2,S.x1-A.x2),max(A.y1-S.y2,S.y1-A.y2)); if(D<0) D=0; ans+=D; S.x1-=D; S.x2+=D; S.y1-=D; S.y2+=D; rectangle Part; Part.x1=max(S.x1,A.x1); Part.x2=min(S.x2,A.x2); Part.y1=max(S.y1,A.y1); Part.y2=min(S.y2,A.y2); S=Part; } cout<<ans; return 0; }