您的位置:首页 > 其它

POJ 1160 Post Office 四边形不等式优化DP

2015-02-26 22:11 369 查看
题意:在一条直线上有N个城市,给出每个城市到原点的距离。现在在一些城市设M个邮局,使每个城市到邮局的距离之和最小。

思路:首先要知道的是,如果N个城市设立一个邮局,邮局要位于中间(奇数为中间的一个,偶数位中间两个的任意一个)的城市上,才能使每个城市到邮局的距离之和最小。

设w[i][j] w[i][j]为在第i个城市到第j个城市之间修建一个邮局,i-j之间的所有城市都使用这个邮局的最小距离和。

则有:

w[i][j]=w[i][j−1]+d j −d ⌊a+b2 ⌋ w[i][j] = w[i][j-1] + d_j - d_{\lfloor \frac{a+b} 2 \rfloor}

初始值:w[i][i]=0 w[i][i] = 0

为什么上面的式子是正确的呢?

1.当j−1 j-1为奇数时,j j为偶数,如果w[i][j] w[i][j]的邮局选取的位置和w[i][j−1] w[i][j-1]的邮局的位置一样,不会影响最优解。这样,我们只需加上第j j个城市到邮局的距离。

2.当j−1 j-1为偶数时,j j为奇数,如果w[i][j] w[i][j]的邮局选取的位置和w[i][j−1] w[i][j-1]中间两个城市右面的位置一样,同样不会影响最优解。这样,我们也只需加上第j j个城市到邮局的距离。

这样,求出w[i][j] w[i][j]的复杂度为Θ(n 2 ) \Theta(n^2).

设dp[i][j] dp[i][j]为在前j个城市设立i个邮局的最小距离和。

可以很容易得到状态转移方程:

dp[i][j]=min(dp[i−1][k]+w[k+1][j]),1≤k≤j dp[i][j] = min(dp[i-1][k] + w[k+1][j]), 1 \le k \le j

但是这样的时间复杂度为Θ(mn 2 ) \Theta(mn^2)

注意到,设i<i ′ ≤j<j ′ i \lt i' \le j \lt j',则对于w[i][j] w[i][j]满足:

1.区间单调性:w[i ′ ][j]≤w[i][j ′ ] w[i'][j] \le w[i][j']

2.四边形不等式:w[i][j]+w[i ′ ][j ′ ]≤w[i][j ′ ]+w[i ′ ][j] w[i][j] + w[i'][j'] \le w[i][j'] + w[i'][j]

通过上面的两条性质,我们就能证明dp[i][j] dp[i][j]同样满足四边形不等式:

dp[i][j]+dp[i ′ ][j ′ ]≤dp[i][j ′ ]+dp[i ′ ][j] dp[i][j] + dp[i'][j'] \le dp[i][j'] + dp[i'][j]

证明从略。

设s[i][j] s[i][j]表示dp[i][j] dp[i][j]取得最优解时,最后一个邮局设立在城市的编号,即决策变量。

由于dp[i][j] dp[i][j]满足四边形不等式,我们就可以证明s[i][j] s[i][j]具有单调性,即:

s[i][j−1]≤s[i][j]≤s[i+1][j] s[i][j-1] \le s[i][j] \le s[i+1][j]

或者变形为:

s[i−1][j]≤s[i][j]≤s[i][j+1] s[i-1][j]\le s[i][j] \le s[i][j+1]

这样,在求解dp[i][j] dp[i][j]的时候,我们就能利用上面的性质来约束k,即:

dp[i][j]=min(dp[i−1][k]+w[k+1][j]),s[i][j+1]≤k≤s[i+1][j] dp[i][j] = min (dp[i-1][k] + w[k+1][j]), s[i][j+1] \le k \le s[i+1][j]

或:

dp[i][j]=min(dp[i−1][k]+w[k+1][j]),s[i−1][j]≤k≤s[i][j+1] dp[i][j] = min (dp[i-1][k] + w[k+1][j]), s[i-1][j] \le k \le s[i][j+1]

这就优化了算法的时间复杂度,可以证明上面的时间复杂度为Θ(n(n−m))=Θ(n 2 ) \Theta(n(n-m)) = \Theta(n^2)

但是上面两个状态转移方程递推的方向是不同的:

第一个按照区间跨度l递推,最大长度为N-M。

第二个按照i从小到大,j从大到小(因为要利用dp[i][j+1] dp[i][j+1]来得到dp[i][j] dp[i][j]).

代码如下:

#include <cstdio>
#include <algorithm>
#include <cstring>

using namespace std;

const int MAX = 400;

int a[MAX];
int w[MAX][MAX],dp[MAX][MAX],s[MAX][MAX];

int main(void)
{
//freopen("input.txt","r",stdin);
int N,M;
while(scanf("%d%d",&N,&M) != EOF){
for(int i = 1; i <= N; ++i)
scanf("%d",&a[i]);
for(int i = 1; i <= N; ++i){
w[i][i] = 0;
for(int j = i + 1; j <= N; ++j)
w[i][j] = w[i][j-1] + a[j] - a[(i+j) / 2];
}
memset(dp,0x3f,sizeof(dp));
for(int j = 1; j <= N; ++j){
dp[1][j] = w[1][j];
s[1][j] = 0;
}
for(int i = 2; i <= M; ++i){
s[i][N+1] = N;
for(int j = N; j > i; --j){
for(int k = s[i-1][j]; k <= s[i][j+1]; ++k){
if(dp[i-1][k] + w[k+1][j] < dp[i][j]){
dp[i][j] = dp[i-1][k] + w[k+1][j];
s[i][j] = k;
}
}
}
}
printf("%d\n",dp[M]
);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: