邮局村庄问题,n个村庄m个邮局,每个村庄都要去一个离自己最近的邮局,求村庄到邮局的最短总距离
邮局村庄问题,n个村庄m个邮局,每个村庄都要去一个离自己最近的邮局,求村庄到邮局的最短总距离
输入 n,m village
输出 最短距离
该问题是区间dp,首先假设只有一个邮局,则将邮局放在村庄数量的中位数位置可以得到最短距离,
设dis[i][j] 表示第i到j个村庄之间放一个邮局的最短距离,通过下面递推式可以得到任意两村庄之间设置一个邮局的最短距离:
dis[i][j] = dis[i][j-1] + village[j] - village[(i+j)/2]
设dp[i][j]表示前i个村庄分布j个邮局的最短距离,则:
dp[i][j] = dp[k][j-1] + dis[k+1][i]
即i个村庄设置j个邮局的最短距离等于1到k个村庄j-1个邮局的最短距离加上k+1到i个村庄设置一个邮局的距离,循环前i个村庄的k,即可求出最优解。
代码如下
package main
import "fmt"
// 村庄邮局问题, n个村庄, m个邮局, 村庄直线排列, 问如何分布邮局使得村庄到邮局的距离最短
//
func main() {
n, m := 5, 2
village := []int{1, 2, 3, 4, 5}
// dis[i][j] 表示第i-j村庄之间有 1 个邮局的最短距离
// 可以证明 邮局 位于 i j 中位数之间时 距离最短
// 因此 dis[i][j] = dis[i][j-1] + village[j] - village[(i+j)/2]
dis := make([][]int, n+1)
for i := range dis {
dis[i] = make([]int, n+1)
}
for i := 1; i <= n; i++ {
for j := i; j <= n; j++ {
dis[i][j] = dis[i][j-1] + village[j-1] - village[(i-1+j-1)/2]
}
}
//for _, dis := range dis {
// fmt.Println(dis)
//}
// [0 0 0 0 0 0]
// [0 0 1 2 4 6]
// [0 0 0 1 2 4]
// [0 0 0 0 1 2]
// [0 0 0 0 0 1]
// [0 0 0 0 0 0]
// dp[i][j] 表示前i个村庄分布j个邮局的最优解
// dp[i][j] = dp[i-k][j-1] + dis[i-k][i]
// 枚举 邮局数量j来更新dp
dp := make([][]int, n+1)
for i := 0; i <= n; i++ {
dp[i] = make([]int, m+1)
dp[i][1] = dis[1][i]
}
for j := 2; j <= m; j++ {
for i := j; i <= n; i++ {
dp[i][j] = 1000007
for k := j - 1; k < i; k++ {
dp[i][j] = min(dp[i][j], dp[k][j-1]+dis[k+1][i])
}
}
}
for _, dp := range dp {
fmt.Println(dp)
}
//[0 0 0]
//[0 0 0]
//[0 1 1]
//[0 2 2]
//[0 4 3]
//[0 6 4]
}
func min(a, b int) int {
if a > b {
return b
}
return a
}

浙公网安备 33010602011771号