題目大意是給定n個(gè)點(diǎn)的坐標(biāo)(n <= 10000),問把這些點(diǎn)移動(dòng)到一橫行并且一個(gè)挨著一個(gè)(具體位置任意)的最少移動(dòng)步數(shù)(其中每次只能向上下左右移動(dòng)一個(gè)坐標(biāo))。
這個(gè)題目體現(xiàn)了轉(zhuǎn)化的思想。首先考慮這樣的問題:一個(gè)數(shù)軸上有n個(gè)坐標(biāo),問把這n個(gè)坐標(biāo)移動(dòng)到一個(gè)點(diǎn)上最少移動(dòng)步數(shù),其中每次移動(dòng)一個(gè)格子。根據(jù)中位數(shù)的定義,把所有坐標(biāo)排序后第n / 2個(gè)坐標(biāo)是中位數(shù),把所有坐標(biāo)移動(dòng)到這上面移動(dòng)次數(shù)最小。證明很容易想到,因?yàn)槿绻贿@樣的話,把目標(biāo)坐標(biāo)往左平移還是往右平移,勢必造成左半部的坐標(biāo)集體變化1,右半部的坐標(biāo)也集體變化1,如果左右半部坐標(biāo)的個(gè)數(shù)不同,那么顯然就不是最優(yōu)的了。
接下來考慮題目,題目中x和y的移動(dòng)是孤立的,可以分開討論。y的移動(dòng)方法和上面討論的情況一樣,現(xiàn)在考慮x的移動(dòng)。x的移動(dòng)要求最終是一個(gè)挨著一個(gè)的,x排好序之后,假設(shè)最終所有點(diǎn)以x0為左端點(diǎn)依次排開,對(duì)應(yīng)的點(diǎn)分別為x0, x1...那么問題的答案就等于把這n個(gè)坐標(biāo)依次對(duì)應(yīng)的挪到x0到xn-1上的步數(shù)。如果我們把這n個(gè)目標(biāo)點(diǎn)分別都移動(dòng)到x0上,那么問題就轉(zhuǎn)化成了中位數(shù)問題了。考慮把xi移動(dòng)到x0上,要花費(fèi)i步,為了保證問題是等價(jià)變換的,應(yīng)該把xi在原坐標(biāo)中對(duì)應(yīng)的xi'也相應(yīng)的向左移動(dòng)i步,這樣xi'移動(dòng)到xi的代價(jià)就是不變的。設(shè)xi'左移i步后的新位置是xi'',那么問題就轉(zhuǎn)化成:把x0''到xn-1''這n個(gè)點(diǎn)移動(dòng)到一個(gè)坐標(biāo)的最小步數(shù),用中位數(shù)的方法就可以做出來了。
這個(gè)題目的巧妙之處在于把一個(gè)未知問題轉(zhuǎn)化成一個(gè)已知問題。轉(zhuǎn)化的思想在數(shù)學(xué)中用的很多,應(yīng)該多多練習(xí)。
題目代碼:
#include <algorithm>
using namespace std;
const int N = 10010;
int main()
{
int x[N], y[N], n;
while (scanf("%d", &n) == 1)
{
for (int i = 0; i < n; i++)
scanf("%d %d", &x[i], &y[i]);
sort(x, x + n);
sort(y, y + n);
for (int i = 0; i < n; i++)
x[i] -= i;
sort(x, x + n);
int ans = 0;
for (int i = 0; i < n / 2; i++)
ans += x[n-i-1] - x[i] + y[n-1-i] - y[i];
printf("%d\n", ans);
}
return 0;
}