POJ1160 Post Office
题目描述
在笔直的大道上建有 n n n个大别墅,两个大别墅之间的距离是他们的横坐标之差的绝对值,保证大别墅之间的距离只能是整数,且没有别墅的位置是相同的。
现在需要建立 m m m座大超市 ( m ≤ n ) (m \leq n) (m≤n),大超市只能建立在别墅所在的位置,现在需要你写个程序,给定了所有别墅的位置和大超市的数目,计算出每个别墅离最近的大超市的距离总和的最小值。
输入格式
第一行包括两个整数 n n n和 m m m
第二行包括n个整数,代表别墅的位置,以升序的形式列出。对于每一个整数 x x x.
样例输入
10 5
1 2 4 6 7 10 11 25 44 70
样例输出
11
数据范围
1 < = n < = 300 1 <= n <= 300 1<=n<=300
1 < = m < = 30 1 <= m <= 30 1<=m<=30
m < = n m <= n m<=n
1 < = X < = 10000 1 <= X <= 10000 1<=X<=10000
我们先考虑这样一个问题:三个别墅分别在
(
1
,
7
,
20
)
(1, 7, 20)
(1,7,20)中选一个超市,最小的距离和是多少。
答案是在中间那个超市,因为
(
20
−
1
+
1
)
÷
2
=
10
(20-1+1) ÷ 2 = 10
(20−1+1)÷2=10,离
10
10
10最近的超市是7,所以我们选中间这个别墅做超市,那么再来有5个别墅的情况:
答案还是离
(
20
−
1
+
1
)
÷
2
=
10
(20-1+1) ÷ 2=10
(20−1+1)÷2=10的最近的别墅7,所以我们得出一个结论: 只有超市位于中间位置,距离和才最小
注意这个位置不是中间的那个别墅,而是离中间的那个地方最近的别墅。
设 d p [ i ] [ j ] dp[i][j] dp[i][j]表示考虑前 i i i个别墅建 j j j个超市的最小代价。
那么 d p [ i ] [ j ] dp[i][j] dp[i][j]可以从哪个地方转移过来呢?
d p [ k ] [ j − 1 ] dp[k][j - 1] dp[k][j−1],因为我们可以把 k k k前面的别墅分成一块 k k k到 i i i的别墅另外分成一块,这一块只建一个超市。
所以状态转移方程是:
d
p
[
i
]
[
j
]
=
m
i
n
(
d
p
[
i
]
[
j
]
,
d
p
[
k
]
[
j
−
1
]
+
d
i
s
[
k
+
1
]
[
i
]
)
dp[i][j]=min(dp[i][j],\text{ }\text{ }dp[k][j-1]+dis[k+1][i])
dp[i][j]=min(dp[i][j], dp[k][j−1]+dis[k+1][i])
注:
d
i
s
[
i
]
[
j
]
dis[i][j]
dis[i][j]表示第i个别墅到第j个别墅只建一个超市的最小代价
这个 d i s [ i ] [ j ] dis[i][j] dis[i][j]是需要预处理的,因为如果不预处理,每次就需要枚举最优决策点,时间复杂度为 O ( P 2 V 2 ) O(P^2 V^2) O(P2V2)是会超时的,但是我们可以用 V 2 V^2 V2预处理 d i s [ i ] [ j ] dis[i][j] dis[i][j],那么时间复杂度就降成 O ( P 2 V ) O(P^2V) O(P2V)就不会超时了。
那现在来看看这个 d i s [ i ] [ j ] dis[i][j] dis[i][j]怎么算:
我们先去掉 i i i~ j j j区间的最后一个,即只剩下了 i i i ~ j − 1 j-1 j−1,这个区间只建一个超市的最小代价是 d i s [ i ] [ j − 1 ] dis[i][j-1] dis[i][j−1],最后加上新加的这个节点的与最优决策点的差就行了。
#include <bits/stdc++.h>
using namespace std;
#define MAXN 305
#define INF 0x3f3f3f3f
int V, P;
int x[MAXN], dp[MAXN][MAXN], dis[MAXN][MAXN];
int main()
{
scanf("%d %d", &V, &P);
memset(dp, INF, sizeof(dp));
for(int i = 1; i <= V; i++)
scanf("%d", &x[i]);
for(int i = 1; i <= V; i++)
dis[i][i] = 0;
for(int i = 1; i <= V; i++)
for(int j = i + 1; j <= V; j++)
dis[i][j] = dis[i][j - 1] + x[j] - x[(i + j) / 2];
for(int i = 1; i <= V; i++)
dp[i][1] = dis[1][i];
for(int j = 1; j <= P; j++)
for(int i = j + 1; i <= V; i++)
for(int k = 1; k <= i; k++)
dp[i][j] = min(dp[i][j], dp[k][j - 1] + dis[k + 1][i]);
cout << dp[V][P] << endl;
return 0;
}