UVaLive 3708 Graveyard 墓地雕塑

来源:互联网 发布:河南知满天 编辑:程序博客网 时间:2024/04/27 13:58

题意:在一个周长为10000的圆上等距分布着n个雕塑。现又要加入m个雕塑,且使得这n + m个雕塑仍然等距分布。这就需要移动原来的某些雕塑。求移动原来的雕塑的距离和的最小值。



这道题需要分析和证明思路的正确性。直观看来,加入m个雕塑后这n + m个雕塑的位置相对而言应该是确定的(因为等距分布),而且原来的雕像只需要移动到加入雕像后的某个位置的雕像,且是最近的那个。看起来应该是正确的思路,但我们需要证明。


首先,我们假设原来的n个雕像全部都要移动。设原来n个雕像所在的点的集合为A,移动之后原来的n个雕像所在的点的集合为B。我们不妨假设集合A中有不少于一半的点是向逆时针方向移动的,那么我们将集合B中的这n个点每个点都不断的向顺时针方向移动相同的距离,显然不改变题目所要求的状态(因为新加入的m个点不参与计算移动的距离),且移动后与原来移动的总距离相比不会增加,可以理解为更优解。这样不断的移动,总可以在某个时候使B中的某个点与A中的某个点重合。这样即等价于A中至少有一个雕像没有移动。


这样,我们的思路就可以进一步了,就是原来的n个点至少有一个点是可以不动的,不妨设这个点的位置是0点时刻方向的位置。既然这个点不动,因为所有点等距分布,那么剩下的点的位置也就都是绝对确定的了。我们设在这个思路下,移动其他点之后以及加入了新的m个点之后,所有的n + m个点的集合为C。那么我们接下来证明,按照贪心思想,A集合剩下的n - 1个点每个点都移动到离它最近的C集合中的点,不会有两个点移动到同一个位置。


用反证法。假设A集合中有两个点X,Y移动到了C集合的同一个点Z。设C集合里与Z相邻的两个点为ZL,ZR。显然X,Y是分别夹在Z与ZL之间,Z与ZR之间的。我们不放假设X在Z与ZL之间,Y在Z与ZR之间。因为按照贪心思想,X到Z的距离(设为Dx)一定小于等于X到ZL的距离。进一步,可知Dx ≤ |Z - ZL |/2(用|A - B|表示点A和点B之间的距离,下同)。同理,Y到Z的距离Dy ≤ |Z - ZR|/2。显然|Z - ZL| = |Z - ZR|(因为等距分布),我们假设这个距离为Dz。所以Dx + Dy ≤ Dz。另一方面,Dx + Dy可以表示原来等距的n个点里相邻两点的距离,而Dz是表示现在等距的n + m个点里相邻两点的距离,显然加入点之后这个距离应该更小,也就是应该满足Dx + Dy > Dz,这样得出了矛盾。所以不会有两个点移动到同一个位置。


至此问题就解决了。计算的时候我们用一个小技巧。假设现在n + m个点的等距为“单位1”(实际上是10000/(n + m)),我们按1计算出答案后乘上10000/(n + m)即可。因为现在n + m个点的等距为1,那么原来n个点的等距为(n + m)/n。不妨设那个不需要移动的点的坐标为0,那么现在n + m个点(C集合里所有点)的坐标为0 ~ n + m - 1,原来n个点的坐标(A集合中的点),第i个点为i*(n + m)/n,将这个坐标四舍五入得到的整数点即是离它最近的C集合里的点的坐标,相减取绝对值后加入总和即可求出答案,最后乘上10000/(n + m)即为最终答案。


#include <iostream>#include <cstdio>#include <cmath>#include <cstring>#include <string>#include <algorithm>#include <stack>#include <queue>#include <vector>#include <map>#include <set>using namespace std;int n, m;void solve(){    double ans = 0;    double original = (double)(n + m)/n; //原来n个点的等距    for(int i = 0; i <= n; i++)    {        double temp = i*original;        ans += fabs(temp - int(temp + 0.5)); // + 0.5是取四舍五入的技巧    }    printf("%.4lf\n", ans*10000/(n + m));}int main(){    while(scanf("%d%d", &n, &m) != EOF)        solve();    return 0;}



0 0