SPOJ_1873
这个题目的子问题实际上是SGU_114一类的题目(http://www.cnblogs.com/staginner/archive/2012/01/11/2319989.html)。
首先可以把题意转化为每次可以将一个或一堆连在一起的方块向左移一个位置或者向右移一个位置,最后的代价就是各个方块移动的长度之和。可以证明,最优解中,每个方块应该都是向着某个点移动的,不会说先左移移再右移移之类的,那样一定不如一开始就向着一个方向移划得来。
这样根据移动的方向不同,那么这个环就可以等价成一条链,只不过现在还不明白从哪里断开。
不过不妨先加假设成现在已经断成了一条链,那么这些方块该向着那个点移动呢?细想之后就会发现和SGU_114是一样的,向着中间的那个方块移动就行了。这样我们可以O(1)的时间求出这条链上方块聚集到一起的最小代价。
既然不知道从哪里断开,不妨就枚举断开的位置,或者更方便一点,枚举向着哪个方块移动,然后左边(N-1)/2个方块向右移,右边N/2个方块向左移就行了,这样整体就是O(N)的复杂度。
#include<stdio.h> #include<string.h> #include<algorithm> #define MAXD 200010 using namespace std; int N, L, a[MAXD]; long long A[MAXD], AA[MAXD], B[MAXD], BB[MAXD]; void init() { int i, j, k; a[0] = -1; for(i = 1; i <= N; i ++) scanf("%d", &a[i]); sort(a + 1, a + 1 + N); for(i = N + 1; i <= (N << 1); i ++) a[i] = a[i - N] + L; a[N << 1 | 1] = L << 1; A[0] = 0; for(i = 1; i <= (N << 1); i ++) A[i] = A[i - 1] + a[i] - a[i - 1] - 1; B[N << 1 | 1] = 0; for(i = N << 1; i > 0; i --) B[i] = B[i + 1] + a[i + 1] - a[i] - 1; AA[0] = 0; for(i = 1; i <= (N << 1); i ++) AA[i] = AA[i - 1] + A[i]; BB[N << 1 | 1] = 0; for(i = N << 1; i > 0; i --) BB[i] = BB[i + 1] + B[i]; } long long Min(long long x, long long y) { return x < y ? x : y; } void solve() { int i, j, k, ln = N - 1 >> 1, rn = N >> 1, x, y; long long ans = 1ll << 62, tl, tr; for(i = 1; i <= N; i ++) { tl = BB[i + N - ln] - BB[i + N] - ln * B[i + N]; tr = AA[i + rn] - AA[i] - rn * A[i]; ans = Min(ans, tl + tr); } printf("%lld\n", ans); } int main() { for(;;) { scanf("%lld%lld", &N, &L); if(N == 0 && L == 0) break; init(); solve(); } return 0; }